diff --git a/CMakeLists.txt b/CMakeLists.txt index d8e33b5..7606a98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,6 @@ include_directories( ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/profiling/ ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/runtime/ ${ASCEND_HOME_PATH}/${ARCH}-linux/include/ - ${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl/ ${ASCEND_HOME_PATH}/${ARCH}-linux/include/experiment ${ASCEND_HOME_PATH}/${ARCH}-linux/include/experiment/runtime/ ${ASCEND_HOME_PATH}/${ARCH}-linux/include/experiment/msprof/ diff --git a/docs/CCU_ASCENDC_HANDOFF.md b/docs/CCU_ASCENDC_HANDOFF.md new file mode 100644 index 0000000..b38f699 --- /dev/null +++ b/docs/CCU_ASCENDC_HANDOFF.md @@ -0,0 +1,222 @@ +# TileXR Direct CCU Handoff + +This document is the maintained handoff for TileXR-owned direct CCU support. +It replaces the earlier investigation log. Keep it factual and update it only +when the production path, public API, or validation gate changes. + +## Current Status + +TileXR can prepare, install, submit, and validate a two-rank direct CCU flow +without a production dependency on hcomm, HCCL private CCU producers, or +`libmc2_client.so`. + +Validated path: + +- Direct CCU repository and mission installation. +- Mission/key/taskInfo generation for `rtCCULaunch`. +- XN, CKE, channel, PFE, and jetty lower-layer resource installation. +- Barrier smoke through CCU synchronization instructions. +- P2P data-plane validation through CCU memory-copy microcode. + +Completion evidence from the 950 validation server: + +```text +tilexr_ccu_direct_evening_smoke finalStatus prepare=pass submit=pass barrier=pass p2p=pass +tilexr_ccu_direct_smoke p2pCcuCopy ... mismatches=0 ... passed=1 +TileXRDirectCcuTrace program.sync[5] decoded=TransRmtMemToLocMem ... +TileXR CCU dependency guard passed: no hcomm/HCCL private CCU dependency or symbol reference +``` + +## Production Boundary + +`src/comm` must not link or include hcomm or HCCL private CCU producer APIs. +The allowed production dependency surface is CANN runtime/ACL plus TileXR-owned +CCU code under `src/comm/ccu`. + +Keep these out of production code: + +- `libhcomm.so`, `libhccl_v2.so`, `libhccl_fwk.so`, `libmc2_client.so` +- `HcclGetCcuTaskInfo`, `HcomGetCcuTaskInfo` +- hcomm private resource allocators, repositories, and channel abstractions +- AscendC kernel-side `Hccl` integration until TileXR has + a stable TileXR-owned context producer for that exact ABI + +Run the dependency guard after every CCU production edit: + +```bash +source scripts/common_env.sh +bash tests/ccu/check_tile_comm_no_hcomm_deps.sh build/src/comm/libtile-comm.so +``` + +## Architecture + +Main modules: + +- `tilexr_ccu_direct_runtime.*`: owns direct runtime interaction and basic CCU + information discovery. +- `tilexr_ccu_driver_adapter.*`: wraps the low-level custom-channel driver + operations used for CCU resource and repository installation. +- `tilexr_ccu_hccp_loader.*`: resolves TileXR-owned HCCP/RA entry points. +- `tilexr_ccu_install_provider.*`: installs lower-layer CCU resources, + repository images, and missions. +- `tilexr_ccu_repository.*`: builds and uploads the instruction repository. +- `tilexr_ccu_resource_allocator.*`: reserves mission, instruction, XN, CKE, + GSA, and channel ranges. +- `tilexr_ccu_producer_plan.*`: builds the direct CCU program and submit tasks. +- `tilexr_ccu_barrier_program.*`: emits synchronization microcode. +- `tilexr_ccu_memory_program.*`: emits CCU memory-copy microcode. +- `tilexr_ccu_direct_orchestrator.*`: joins allocation, lower-layer planning, + repository installation, mission installation, and launch-package creation. + +Host integration: + +- `TileXRComm::PrepareDirectCcuInstallAttempt(...)` prepares generic direct CCU + tasks. +- `TileXRComm::PrepareDirectCcuMemoryCopyInstallAttempt(...)` prepares P2P CCU + copy tasks. +- `TileXRCommPrepareDirectCcu(...)` and + `TileXRCommPrepareDirectCcuMemoryCopy(...)` expose the public C API. + +Runtime launch: + +- The final launch path uses `TileXRDirectCcuSubmitPrepared(...)` or + `TileXRDirectCcuSubmitPreparedTask(...)`. +- `rtCCULaunch` is reached through `libruntime.so`; no hcomm launch wrapper is + required. + +## Public API + +`src/include/tilexr_api.h` exposes: + +```c +int TileXRCommPrepareDirectCcu( + TileXRCommPtr comm, + const TileXRDirectCcuPrepareOptions* options, + TileXRDirectCcuPreparedTasksPtr* prepared, + TileXRDirectCcuPrepareReport* report); + +int TileXRCommPrepareDirectCcuMemoryCopy( + TileXRCommPtr comm, + const TileXRDirectCcuMemoryCopyPrepareOptions* options, + TileXRDirectCcuPreparedTasksPtr* prepared, + TileXRDirectCcuPrepareReport* report); + +int TileXRDirectCcuSubmitPrepared( + TileXRDirectCcuPreparedTasksPtr prepared, + aclrtStream stream, + TileXRDirectCcuSubmitReport* report); + +int TileXRDirectCcuSubmitPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t taskIndex, + aclrtStream stream, + TileXRDirectCcuSubmitReport* report); + +int TileXRDirectCcuDestroyPrepared(TileXRDirectCcuPreparedTasksPtr prepared); +``` + +Memory-copy direction constants: + +```c +TILEXR_DIRECT_CCU_MEMORY_COPY_REMOTE_TO_LOCAL +TILEXR_DIRECT_CCU_MEMORY_COPY_LOCAL_TO_REMOTE +``` + +The direct P2P smoke currently validates remote-to-local transfer on both ranks. + +## Key Lessons + +- The AscendC CCU `HcclCombineOpParam` route is not a validated TileXR + integration path. Public MC2 allocation probes returned AICPU/MC2 context + shapes, not the CCU context consumed by `HCCL_SERVER_TYPE_CCU`. +- `rtCcuTaskInfo_t` and `rtCCULaunch` are reachable through CANN runtime, but + the hard part is producing the repository, mission key, lower-layer resources, + and task fields correctly. +- Repository installation requires the RA context resource-window route. A zero + or synthetic resource-window token can let lower-layer calls appear partially + valid while `SET_INSTRUCTION` still fails. +- The passing lower-layer route uses hcomm-compatible semantics without linking + hcomm: reverse endpoint EID and imported peer TPN are important. +- P2P validation must use CCU copy microcode. The old marker/IPC harness did not + prove the direct CCU data plane and has been removed from the maintained smoke. +- Process bring-up overrides for runtime task fields, submit-task arguments, + peer remote-XN/CKE bindings, and hcomm-trace resource remapping were removed + from maintained code. The formal path relies on exchanged TileXR resource + evidence. +- CCU direct work is independent of UDMA. Do not use UDMA code as a reference + for CCU resource installation or data movement. +- CANN 9.1 runtime declares `rtUbDevQueryInfo` in + `runtime/rts/rts_device.h`; the `tile-comm` target must include + `${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/runtime/runtime/`. + +## Maintained Validation + +Local source/unit checks: + +```bash +python -m unittest \ + tests.ccu.test_tilexr_ccu_direct_smoke_probe \ + tests.ccu.test_tilexr_ccu_public_comm_api \ + tests.ccu.test_tilexr_ccu_direct_smoke_runner \ + tests.ccu.test_tilexr_ccu_direct_orchestrator \ + tests.ccu.test_tilexr_ccu_memory_program \ + tests.ccu.test_tilexr_ccu_microcode +``` + +Broader CCU checks: + +```bash +python -m unittest discover tests/ccu +``` + +Remote build and no-hcomm guard: + +```bash +source scripts/common_env.sh +cmake --build build --target tile-comm -j2 +bash tests/ccu/check_tile_comm_no_hcomm_deps.sh build/src/comm/libtile-comm.so +``` + +Hardware smoke: + +```bash +source scripts/common_env.sh +env \ + TILEXR_CCU_SMOKE_DEVICES=0,1 \ + TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU=1 \ + TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE=ra_ctx \ + TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES=prepare,submit,barrier,p2p \ + TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES='ra_ctx_full:acl:full_repository:instruction_bytes:lower_layer_first' \ + TILEXR_CCU_EVENING_TOTAL_TIMEOUT=180 \ + bash tests/ccu/run_tilexr_ccu_direct_evening_smoke.sh +``` + +Expected final line: + +```text +tilexr_ccu_direct_evening_smoke finalStatus prepare=pass submit=pass barrier=pass p2p=pass +``` + +Expected P2P evidence in rank logs: + +```text +tilexr_ccu_direct_smoke p2pCcuCopy ... mismatches=0 ... passed=1 +decoded=TransRmtMemToLocMem +``` + +## Development Rules + +- Keep direct CCU code under `src/comm/ccu` and public API declarations under + `src/include/tilexr_api.h`. +- Keep hardware smoke scripts default-safe; they must require explicit env + opt-in before touching NPU devices. +- Do not reintroduce marker-based P2P success criteria. It does not prove CCU + transfer. +- Do not add runtime env overrides that mutate prepared task fields, prepared + task arguments, or peer binding proof. They make the smoke hard to interpret + and are not a production integration path. +- Keep verbose CCU instruction/resource traces behind env gates. They are useful + for hardware failures, but should not be required for normal validation. +- Prefer small unit tests for microcode, repository layout, resource allocation, + and submit-task packaging. Use hardware smoke only for the final device-plane + proof. diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index fe13647..91c436f 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -105,6 +105,44 @@ set(TILEXR_SOURCE_FILE tilexr_comm.cpp tilexr_internal.cpp tilexr_internal.h tilexr_log.h + ccu/tilexr_ccu_abi_constants.h + ccu/tilexr_ccu_barrier_program.h + ccu/tilexr_ccu_barrier_program.cpp + ccu/tilexr_ccu_direct_orchestrator.h + ccu/tilexr_ccu_direct_orchestrator.cpp + ccu/tilexr_ccu_direct_runtime.h + ccu/tilexr_ccu_direct_runtime.cpp + ccu/tilexr_ccu_driver_adapter.h + ccu/tilexr_ccu_driver_adapter.cpp + ccu/tilexr_ccu_hccp_types.h + ccu/tilexr_ccu_hccp_loader.h + ccu/tilexr_ccu_hccp_loader.cpp + ccu/tilexr_ccu_launch_package.h + ccu/tilexr_ccu_launch_package.cpp + ccu/tilexr_ccu_install_provider.h + ccu/tilexr_ccu_install_provider.cpp + ccu/tilexr_ccu_lower_layer_plan_builder.h + ccu/tilexr_ccu_lower_layer_plan_builder.cpp + ccu/tilexr_ccu_lower_layer_payloads.h + ccu/tilexr_ccu_lower_layer_payloads.cpp + ccu/tilexr_ccu_memory_program.h + ccu/tilexr_ccu_memory_program.cpp + ccu/tilexr_ccu_microcode.h + ccu/tilexr_ccu_microcode.cpp + ccu/tilexr_ccu_producer_plan.h + ccu/tilexr_ccu_producer_plan.cpp + ccu/tilexr_ccu_provider.h + ccu/tilexr_ccu_provider.cpp + ccu/tilexr_ccu_ra_custom_channel_provider.h + ccu/tilexr_ccu_ra_custom_channel_provider.cpp + ccu/tilexr_ccu_repository.h + ccu/tilexr_ccu_repository.cpp + ccu/tilexr_ccu_resource_allocator.h + ccu/tilexr_ccu_resource_allocator.cpp + ccu/tilexr_ccu_runtime.h + ccu/tilexr_ccu_runtime.cpp + ccu/tilexr_ccu_specs.h + ccu/tilexr_ccu_specs.cpp tools/socket/tilexr_sock_exchange.h tools/socket/tilexr_sock_exchange.cpp udma/tilexr_hccp_defs.h @@ -119,6 +157,11 @@ set(TILEXR_SOURCE_FILE tilexr_comm.cpp ) add_library(tile-comm SHARED ${TILEXR_SOURCE_FILE}) +set_target_properties(tile-comm PROPERTIES + BUILD_WITH_INSTALL_RPATH FALSE + SKIP_BUILD_RPATH TRUE + INSTALL_RPATH "" + SKIP_INSTALL_RPATH TRUE) if(TILEXR_HAVE_PTO_SDMA) target_compile_definitions(tile-comm PRIVATE TILEXR_HAVE_PTO_SDMA=1) @@ -134,6 +177,7 @@ target_include_directories(tile-comm ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/ ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/profiling/ ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/runtime/ + ${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/runtime/runtime/ ${ASCEND_HOME_PATH}/${ARCH}-linux/include/ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/src/comm/ccu/tilexr_ccu_abi_constants.h b/src/comm/ccu/tilexr_ccu_abi_constants.h new file mode 100644 index 0000000..10b1692 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_abi_constants.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_ABI_CONSTANTS_H +#define TILEXR_CCU_ABI_CONSTANTS_H + +#include + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_EID_BYTES = 16; +constexpr uint32_t TILEXR_CCU_REMOTE_CCU_VA_SHIFT = 23; + +} // namespace TileXR + +#endif // TILEXR_CCU_ABI_CONSTANTS_H diff --git a/src/comm/ccu/tilexr_ccu_barrier_program.cpp b/src/comm/ccu/tilexr_ccu_barrier_program.cpp new file mode 100644 index 0000000..319caee --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_barrier_program.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_barrier_program.h" + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuBarrierProgramReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuBarrierProgramReport{}; + } +} + +int Fail( + std::vector* program, + TileXRCcuBarrierProgramReport* report, + const std::string& message) +{ + if (program != nullptr) { + program->clear(); + } + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +bool MissingPostResource(const TileXRCcuBarrierSyncSpec& spec) +{ + return spec.remoteXn == 0 || spec.localXn == 0 || spec.channelId == 0 || + spec.remoteNotifyCke == 0 || spec.remoteNotifyMask == 0; +} + +bool MissingSyncCkePostResource(const TileXRCcuBarrierSyncSpec& spec) +{ + return spec.channelId == 0 || spec.remoteNotifyCke == 0 || spec.remoteNotifyMask == 0 || + spec.sourceCke == 0 || spec.sourceCkeMask == 0; +} + +bool MissingWaitResource(const TileXRCcuBarrierSyncSpec& spec) +{ + return spec.localWaitCke == 0 || spec.localWaitMask == 0; +} + +void FillReport( + size_t specCount, + size_t totalInstructionCount, + TileXRCcuBarrierProgramReport* report, + bool hasWaitInstructions = true) +{ + if (report == nullptr) { + return; + } + report->postInstructionCount = static_cast(specCount); + report->waitInstructionCount = hasWaitInstructions ? static_cast(specCount) : 0U; + report->totalInstructionCount = static_cast(totalInstructionCount); + report->message = "ok"; +} + +bool LoadBeforePostOnly(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +bool PostOnly(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly || + mode == TileXRCcuBarrierMode::SyncCkePostOnly || + mode == TileXRCcuBarrierMode::LocalCkePostOnly; +} + +bool SyncCkeMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncCke || + mode == TileXRCcuBarrierMode::SyncCkeSetWait || + mode == TileXRCcuBarrierMode::SyncCkePostOnly; +} + +bool SyncCkeSetWaitMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncCkeSetWait; +} + +void AddSourceCkeInitInstructions( + const std::vector& specs, + std::vector* program) +{ + std::vector initialized; + for (const auto& spec : specs) { + bool seen = false; + for (uint16_t cke : initialized) { + if (cke == spec.sourceCke) { + seen = true; + break; + } + } + if (seen) { + continue; + } + TileXRCcuCkeSpec init; + init.ckeId = spec.sourceCke; + init.mask = spec.sourceCkeMask; + init.clearWait = true; + TileXRCcuInstr instr; + if (TileXRCcuEncodeSetCke(init, &instr) == TILEXR_SUCCESS) { + program->push_back(instr); + initialized.push_back(spec.sourceCke); + } + } +} + +int AddLocalCkeDiagnosticInstructions( + const std::vector& specs, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + for (const auto& spec : specs) { + TileXRCcuCkeSpec post; + post.ckeId = spec.localWaitCke; + post.mask = spec.localWaitMask; + post.clearWait = false; + TileXRCcuInstr instr; + if (TileXRCcuEncodeSetCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode local CKE diagnostic set instruction"); + } + program->push_back(instr); + } + + for (const auto& spec : specs) { + TileXRCcuCkeSpec wait; + wait.waitCkeId = spec.localWaitCke; + wait.waitMask = spec.localWaitMask; + wait.clearWait = spec.clearLocalWait; + TileXRCcuInstr instr; + if (TileXRCcuEncodeClearCke(wait, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode local CKE diagnostic wait instruction"); + } + program->push_back(instr); + } + + return TILEXR_SUCCESS; +} + +int AddLocalCkePostOnlyDiagnosticInstructions( + const std::vector& specs, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + for (const auto& spec : specs) { + TileXRCcuCkeSpec post; + post.ckeId = spec.localWaitCke; + post.mask = spec.localWaitMask; + post.clearWait = false; + TileXRCcuInstr instr; + if (TileXRCcuEncodeSetCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode local CKE post-only diagnostic set instruction"); + } + program->push_back(instr); + } + return TILEXR_SUCCESS; +} + +} // namespace + +int TileXRCcuBuildBarrierProgram( + const std::vector& specs, + std::vector* program, + TileXRCcuBarrierProgramReport* report, + TileXRCcuBarrierMode mode) +{ + ResetReport(report); + if (program == nullptr) { + return Fail(program, report, "missing output CCU barrier program"); + } + program->clear(); + if (specs.empty()) { + return Fail(program, report, "missing CCU barrier sync specs"); + } + + for (const auto& spec : specs) { + if (mode != TileXRCcuBarrierMode::LocalCke && + mode != TileXRCcuBarrierMode::LocalCkePostOnly && + (SyncCkeMode(mode) ? MissingSyncCkePostResource(spec) : + MissingPostResource(spec))) { + return Fail(program, report, "missing remote XN post resource for CCU barrier program"); + } + if (!PostOnly(mode) && MissingWaitResource(spec)) { + return Fail(program, report, "missing local wait CKE resource for CCU barrier program"); + } + } + + program->reserve(specs.size() * (SyncCkeMode(mode) ? 3U : 2U)); + if (mode == TileXRCcuBarrierMode::LocalCke) { + const int ret = AddLocalCkeDiagnosticInstructions(specs, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + FillReport(specs.size(), program->size(), report); + return TILEXR_SUCCESS; + } + if (mode == TileXRCcuBarrierMode::LocalCkePostOnly) { + const int ret = AddLocalCkePostOnlyDiagnosticInstructions(specs, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + FillReport(specs.size(), program->size(), report, false); + return TILEXR_SUCCESS; + } + if (SyncCkeMode(mode)) { + AddSourceCkeInitInstructions(specs, program); + } + + for (const auto& spec : specs) { + TileXRCcuInstr instr; + if (SyncCkeMode(mode)) { + TileXRCcuSyncCkeSpec post; + post.remoteCke = spec.remoteNotifyCke; + post.localCke = spec.sourceCke; + post.localCkeMask = spec.remoteNotifyMask; + post.channelId = spec.channelId; + if (TileXRCcuEncodeSyncCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU barrier SyncCKE post instruction"); + } + } else { + if (LoadBeforePostOnly(mode)) { + if (TileXRCcuEncodeLoadImdToXn(spec.localXn, 1U, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU barrier local XN load instruction"); + } + program->push_back(instr); + } + TileXRCcuSyncXnSpec post; + post.remoteXn = spec.remoteXn; + post.localXn = spec.localXn; + post.channelId = spec.channelId; + post.notifyCke = spec.remoteNotifyCke; + post.notifyMask = spec.remoteNotifyMask; + if (TileXRCcuEncodeSyncXn(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU barrier post instruction"); + } + } + program->push_back(instr); + } + + if (PostOnly(mode)) { + FillReport(specs.size(), program->size(), report, false); + return TILEXR_SUCCESS; + } + + for (const auto& spec : specs) { + TileXRCcuCkeSpec wait; + wait.waitCkeId = spec.localWaitCke; + wait.waitMask = spec.localWaitMask; + wait.clearWait = spec.clearLocalWait; + TileXRCcuInstr instr; + const int ret = SyncCkeMode(mode) && !SyncCkeSetWaitMode(mode) ? + TileXRCcuEncodeClearCke(wait, &instr) : + TileXRCcuEncodeSetCke(wait, &instr); + if (ret != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU barrier wait/clear instruction"); + } + program->push_back(instr); + } + + FillReport(specs.size(), program->size(), report); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_barrier_program.h b/src/comm/ccu/tilexr_ccu_barrier_program.h new file mode 100644 index 0000000..394b468 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_barrier_program.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_BARRIER_PROGRAM_H +#define TILEXR_CCU_BARRIER_PROGRAM_H + +#include "ccu/tilexr_ccu_microcode.h" + +#include +#include +#include + +namespace TileXR { + +enum class TileXRCcuBarrierMode { + SyncXn = 0, + SyncCke = 1, + LocalCke = 2, + SyncXnPostOnly = 3, + SyncXnLoadPostOnly = 4, + SyncCkePostOnly = 5, + LocalCkePostOnly = 6, + SyncCkeSetWait = 7, +}; + +struct TileXRCcuBarrierSyncSpec { + uint16_t remoteXn = 0; + uint16_t localXn = 0; + uint16_t channelId = 0; + uint16_t remoteNotifyCke = 0; + uint16_t remoteNotifyMask = 0; + uint16_t localWaitCke = 0; + uint16_t localWaitMask = 0; + uint16_t sourceCke = 0; + uint16_t sourceCkeMask = 0; + bool clearLocalWait = true; +}; + +struct TileXRCcuBarrierProgramReport { + uint32_t postInstructionCount = 0; + uint32_t waitInstructionCount = 0; + uint32_t totalInstructionCount = 0; + std::string message; +}; + +int TileXRCcuBuildBarrierProgram( + const std::vector& specs, + std::vector* program, + TileXRCcuBarrierProgramReport* report, + TileXRCcuBarrierMode mode = TileXRCcuBarrierMode::SyncXn); + +} // namespace TileXR + +#endif // TILEXR_CCU_BARRIER_PROGRAM_H diff --git a/src/comm/ccu/tilexr_ccu_direct_orchestrator.cpp b/src/comm/ccu/tilexr_ccu_direct_orchestrator.cpp new file mode 100644 index 0000000..f3d8aef --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_orchestrator.cpp @@ -0,0 +1,1199 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_direct_orchestrator.h" + +#include "ccu/tilexr_ccu_runtime.h" + +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr const char* TILEXR_CCU_DIRECT_KNOWN_MISSING_INSTALL_SURFACES = + "remote XN install provider is missing"; +constexpr uint16_t TILEXR_CCU_TRACE_LOAD_SQE_ARGS_TO_X_HEADER = 0x0001U; +constexpr uint16_t TILEXR_CCU_TRACE_LOAD_IMD_TO_GSA_HEADER = 0x0002U; +constexpr uint16_t TILEXR_CCU_TRACE_LOAD_IMD_TO_XN_HEADER = 0x0003U; +constexpr uint16_t TILEXR_CCU_TRACE_SET_CKE_HEADER = 0x0802U; +constexpr uint16_t TILEXR_CCU_TRACE_CLEAR_CKE_HEADER = 0x0804U; +constexpr uint16_t TILEXR_CCU_TRACE_TRANS_RMT_MEM_TO_LOC_MEM_HEADER = 0x1008U; +constexpr uint16_t TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_RMT_MEM_HEADER = 0x1009U; +constexpr uint16_t TILEXR_CCU_TRACE_SYNC_CKE_HEADER = 0x100bU; +constexpr uint16_t TILEXR_CCU_TRACE_SYNC_XN_HEADER = 0x100dU; +constexpr uint64_t TILEXR_CCU_PACKED_TOKEN_VALID_SHIFT = 52ULL; +constexpr uint64_t TILEXR_CCU_PACKED_TOKEN_ID_SHIFT = 32ULL; +constexpr uint64_t TILEXR_CCU_PACKED_TOKEN_ID_MASK = 0xfffffULL; +constexpr uint64_t TILEXR_CCU_PACKED_TOKEN_VALUE_MASK = 0xffffffffULL; +constexpr uint32_t TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT = 7U; +constexpr uint32_t TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT = 3U; +constexpr uint32_t TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT = 2U; + +void ResetReport(TileXRCcuDirectInstallReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + } +} + +void ClearAttempt(TileXRCcuDirectInstallAttempt* attempt) +{ + if (attempt != nullptr) { + *attempt = TileXRCcuDirectInstallAttempt{}; + } +} + +int Fail(TileXRCcuDirectInstallAttempt* attempt, TileXRCcuDirectInstallReport* report, const std::string& message) +{ + ClearAttempt(attempt); + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +bool HasRepositoryInstallInputs(const TileXRCcuDirectInstallOptions& options) +{ + return options.driverAdapter != nullptr && + options.repositoryMemoryOps.alloc != nullptr && + options.repositoryMemoryOps.copyHostToDevice != nullptr && + options.repositoryMemoryOps.free != nullptr; +} + +bool SyncXnMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXn || + mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +bool HasNonZeroArgs(const TileXRCcuTaskWindow& window) +{ + for (uint64_t arg : window.args) { + if (arg != 0) { + return true; + } + } + return false; +} + +bool ContainsRange(uint16_t outerStart, uint16_t outerCount, uint16_t innerStart, uint32_t innerCount) +{ + if (outerCount == 0 || innerCount == 0) { + return false; + } + const uint32_t outerBegin = outerStart; + const uint32_t outerEnd = outerBegin + outerCount; + const uint32_t innerBegin = innerStart; + const uint32_t innerEnd = innerBegin + innerCount; + return innerBegin >= outerBegin && innerEnd <= outerEnd; +} + +bool RangesOverlap(uint16_t firstStart, uint32_t firstCount, uint16_t secondStart, uint32_t secondCount) +{ + const uint32_t firstEnd = static_cast(firstStart) + firstCount; + const uint32_t secondEnd = static_cast(secondStart) + secondCount; + return static_cast(firstStart) < secondEnd && static_cast(secondStart) < firstEnd; +} + +TileXRCcuRange MakeRange(uint8_t dieId, uint16_t startId, uint16_t count) +{ + TileXRCcuRange range; + range.dieId = dieId; + range.startId = startId; + range.num = count; + return range; +} + +uint64_t PackCcuSqeToken(uint32_t tokenId, uint32_t tokenValue, bool valid) +{ + const uint64_t validBits = valid ? 1ULL : 0ULL; + return (validBits << TILEXR_CCU_PACKED_TOKEN_VALID_SHIFT) | + ((static_cast(tokenId) & TILEXR_CCU_PACKED_TOKEN_ID_MASK) << TILEXR_CCU_PACKED_TOKEN_ID_SHIFT) | + (static_cast(tokenValue) & TILEXR_CCU_PACKED_TOKEN_VALUE_MASK); +} + +uint16_t ReadLe16(const uint8_t* raw, uint32_t offset) +{ + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1U]) << 8U); +} + +uint64_t DecodeChannelRemoteCcuVa(const TileXRCcuChannelCtxDataV1& ctx) +{ + const uint16_t word28 = ReadLe16(ctx.raw, 28); + const uint16_t word34 = ReadLe16(ctx.raw, 34); + const uint64_t dstVa = + ((static_cast(word28) >> 8U) & 0xffULL) | + (static_cast(ReadLe16(ctx.raw, 30)) << 8U) | + (static_cast(ReadLe16(ctx.raw, 32)) << 24U) | + ((static_cast(word34) & 0x1ULL) << 40U); + return dstVa << TILEXR_CCU_REMOTE_CCU_VA_SHIFT; +} + +int PopulateHcommStyleSqeTaskArgs( + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || + attempt->plan.taskWindows.size() < 2 || + !SyncXnMode(attempt->plan.barrierMode)) { + return TILEXR_SUCCESS; + } + + TileXRCcuTaskWindow& sqeLoadTask = attempt->plan.taskWindows[0]; + if (sqeLoadTask.argSize != TILEXR_CCU_SQE_ARGS_LEN || + sqeLoadTask.instCnt == 0 || + attempt->specInfo.resourceAddr == 0) { + return TILEXR_SUCCESS; + } + if (HasNonZeroArgs(sqeLoadTask)) { + return TILEXR_SUCCESS; + } + + sqeLoadTask.args.assign(TILEXR_CCU_SQE_ARGS_LEN, 0); + sqeLoadTask.args[0] = attempt->specInfo.resourceAddr; + sqeLoadTask.args[1] = attempt->specInfo.resourceAddr; + + if (!attempt->preparedLowerLayerPlan.msidTokens.empty()) { + const auto& token = attempt->preparedLowerLayerPlan.msidTokens[0]; + sqeLoadTask.args[2] = PackCcuSqeToken(token.tokenId, token.tokenValue, true); + } + for (const auto& channel : attempt->preparedLowerLayerPlan.channels) { + const uint64_t remoteCcuVa = DecodeChannelRemoteCcuVa(channel.ctx); + if (remoteCcuVa != 0) { + sqeLoadTask.args[3] = remoteCcuVa; + break; + } + } + + if (report != nullptr) { + report->message.clear(); + } + return TILEXR_SUCCESS; +} + +bool DirectTraceEnabled() +{ + const char* value = std::getenv("TILEXR_CCU_DIRECT_TRACE"); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +uint16_t TraceSlot(uint64_t word, uint32_t slot) +{ + return static_cast((word >> (slot * 16U)) & 0xffffU); +} + +uint16_t TraceRead16(const uint8_t* raw, uint32_t offset) +{ + return ReadLe16(raw, offset); +} + +uint64_t TraceReadDoorbellVa(const TileXRCcuLocalJettyCtxData& ctx) +{ + uint64_t value = 0; + for (uint32_t word = 0; word < 4U; ++word) { + value |= static_cast(TraceRead16(ctx.raw, word * 2U)) << (word * 16U); + } + return value; +} + +uint64_t TraceLoadImmediate(const TileXRCcuInstr& instr) +{ + return (instr.words[0] >> 32U) | ((instr.words[1] & 0xffffffffULL) << 32U); +} + +void TraceDecodedPfeCtx(size_t index, const TileXRCcuPfeInstall& pfe) +{ + const uint16_t word = TraceRead16(pfe.ctx.raw, 2); + const uint16_t jettyCountMinusOne = word & 0x7fU; + std::cerr << "TileXRDirectCcuTrace lowerLayerPfe[" << index << "]" + << " decoded=PfeCtx" + << " dieId=" << static_cast(pfe.dieId) + << " pfeOffset=" << pfe.pfeOffset + << " startTaJettyId=" << TraceRead16(pfe.ctx.raw, 0) + << " jettyCount=" << static_cast(jettyCountMinusOne) + 1U + << " jettyCountMinusOne=" << jettyCountMinusOne + << " startLocalJettyCtxId=" << ((word >> 7U) & 0x7fU) + << "\n"; +} + +void TraceDecodedLocalJettyCtx(size_t jettyIndex, size_t ctxIndex, const TileXRCcuLocalJettyCtxData& ctx) +{ + const uint16_t word8 = TraceRead16(ctx.raw, 8); + const uint16_t word10 = TraceRead16(ctx.raw, 10); + const uint16_t word14 = TraceRead16(ctx.raw, 14); + const uint16_t word22 = TraceRead16(ctx.raw, 22); + const uint16_t word24 = TraceRead16(ctx.raw, 24); + const uint32_t tokenId = + ((word8 >> 8U) & 0xffU) | + ((static_cast(word10) & 0xfffU) << 8U); + const uint32_t tokenValue = + ((word10 >> 12U) & 0xfU) | + (static_cast(TraceRead16(ctx.raw, 12)) << 4U) | + ((static_cast(word14) & 0xfffU) << 20U); + const uint32_t wqeBasicBlockShift = (word14 >> 12U) & 0xfU; + const uint32_t wqeBasicBlockCount = 1U << wqeBasicBlockShift; + const uint32_t wqeBasicBlockStartId = + ((word22 >> 12U) & 0xfU) | + ((static_cast(word24) & 0xffU) << 4U); + + std::cerr << "TileXRDirectCcuTrace lowerLayerJettyCtx[" << jettyIndex << "," << ctxIndex << "]" + << " decoded=LocalJettyCtx" + << " doorbellVa=" << std::hex << std::showbase << TraceReadDoorbellVa(ctx) + << " doorbellTokenId=" << tokenId + << " doorbellTokenValue=" << tokenValue + << std::dec << std::noshowbase + << " pfeId=" << (word8 & 0xfU) + << " ioDieId=" << ((word8 >> 4U) & 0x1U) + << " doorbellAddrType=" << ((word8 >> 5U) & 0x1U) + << " tokenValueValid=" << ((word8 >> 6U) & 0x1U) + << " sqeBasicBlockLeftShifts=" << wqeBasicBlockShift + << " wqeBasicBlockCount=" << wqeBasicBlockCount + << " inferredSqDepth=" << (wqeBasicBlockCount / 4U) + << " wqeBasicBlockStartId=" << wqeBasicBlockStartId + << " pi=" << TraceRead16(ctx.raw, 16) + << " ci=" << TraceRead16(ctx.raw, 18) + << " maxCi=" << TraceRead16(ctx.raw, 20) + << " oooCqeCnt=" << (word22 & 0xfffU) + << " doorbellSendState=" << ((word24 >> 8U) & 0x3U) + << "\n"; +} + +void TraceDecodedChannelCtxV1(size_t index, const TileXRCcuChannelInstall& channel) +{ + const uint16_t word16 = TraceRead16(channel.ctx.raw, 16); + const uint16_t word18 = TraceRead16(channel.ctx.raw, 18); + const uint16_t word20 = TraceRead16(channel.ctx.raw, 20); + const uint16_t word22 = TraceRead16(channel.ctx.raw, 22); + const uint16_t word24 = TraceRead16(channel.ctx.raw, 24); + const uint16_t word28 = TraceRead16(channel.ctx.raw, 28); + const uint16_t word34 = TraceRead16(channel.ctx.raw, 34); + + const uint32_t tpn = word16 | ((static_cast(word18) & 0xffU) << 16U); + const uint16_t startJettyId = + ((word18 >> 12U) & 0xfU) | + static_cast((word20 & 0xfffU) << 4U); + const uint16_t jettyCountMinusOne = + ((word20 >> 12U) & 0xfU) | + static_cast((word22 & 0x7U) << 4U); + const uint32_t tokenId = + ((static_cast(word22) >> 4U) & 0xfffU) | + ((static_cast(word24) & 0xffU) << 12U); + const uint32_t tokenValue = + ((static_cast(word24) >> 8U) & 0xffU) | + (static_cast(TraceRead16(channel.ctx.raw, 26)) << 8U) | + ((static_cast(word28) & 0xffU) << 24U); + const uint64_t dstVa = + ((static_cast(word28) >> 8U) & 0xffULL) | + (static_cast(TraceRead16(channel.ctx.raw, 30)) << 8U) | + (static_cast(TraceRead16(channel.ctx.raw, 32)) << 24U) | + ((static_cast(word34) & 0x1ULL) << 40U); + const uint64_t remoteCcuVa = dstVa << TILEXR_CCU_REMOTE_CCU_VA_SHIFT; + + std::cerr << "TileXRDirectCcuTrace lowerLayerChannel[" << index << "]" + << " decoded=ChannelCtxV1" + << " dieId=" << static_cast(channel.dieId) + << " channelId=" << channel.channelId + << " tpn=" << std::hex << std::showbase << tpn + << " memoryTokenId=" << tokenId + << " memoryTokenValue=" << tokenValue + << " dstVaShifted=" << dstVa + << " remoteCcuVa=" << remoteCcuVa + << std::dec << std::noshowbase + << " sourcePfeId=" << ((word18 >> 8U) & 0xfU) + << " startTaJettyId=" << startJettyId + << " jettyCount=" << static_cast(jettyCountMinusOne) + 1U + << " jettyCountMinusOne=" << jettyCountMinusOne + << " ioDieId=" << ((word22 >> 3U) & 0x1U) + << " tokenValueValid=" << ((word34 >> 1U) & 0x1U) + << " remoteEid="; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + std::cerr << std::hex << std::setw(2) << std::setfill('0') << std::noshowbase + << static_cast(channel.ctx.raw[i]); + } + std::cerr << std::dec << std::setfill(' ') << "\n"; +} + +void TraceDecodedInstr(const char* label, size_t index, const TileXRCcuInstr& instr) +{ + const uint16_t opcode = TraceSlot(instr.words[0], 0); + std::cerr << "TileXRDirectCcuTrace " << label << "[" << index << "] "; + switch (opcode) { + case TILEXR_CCU_TRACE_LOAD_SQE_ARGS_TO_X_HEADER: + std::cerr << "decoded=LoadSqeArgsToX" + << " xnId=" << TraceSlot(instr.words[0], 1) + << " sqeArgId=" << TraceSlot(instr.words[0], 2); + break; + case TILEXR_CCU_TRACE_LOAD_IMD_TO_XN_HEADER: + std::cerr << "decoded=LoadImdToXn" + << " xnId=" << TraceSlot(instr.words[0], 1) + << " immediate=" << std::hex << std::showbase << TraceLoadImmediate(instr) + << std::dec << std::noshowbase + << " secFlag=" << TraceSlot(instr.words[1], 2); + break; + case TILEXR_CCU_TRACE_LOAD_IMD_TO_GSA_HEADER: + std::cerr << "decoded=LoadImdToGSA" + << " gsaId=" << TraceSlot(instr.words[0], 1) + << " immediate=" << std::hex << std::showbase << TraceLoadImmediate(instr) + << std::dec << std::noshowbase; + break; + case TILEXR_CCU_TRACE_TRANS_RMT_MEM_TO_LOC_MEM_HEADER: { + const uint16_t control = TraceSlot(instr.words[1], 3); + const uint16_t flags = TraceSlot(instr.words[2], 3); + std::cerr << "decoded=TransRmtMemToLocMem" + << " remoteGsa=" << TraceSlot(instr.words[0], 3) + << " remoteXn=" << TraceSlot(instr.words[1], 0) + << " localGsa=" << TraceSlot(instr.words[0], 1) + << " localXn=" << TraceSlot(instr.words[0], 2) + << " lengthXn=" << TraceSlot(instr.words[1], 1) + << " channelId=" << TraceSlot(instr.words[1], 2) + << " clearType=" << (flags & 0x1U) + << " lengthEn=" << ((flags >> 1U) & 0x1U) + << " reduceEn=" << ((flags >> 2U) & 0x1U) + << " reduceDataType=" << ((control >> 8U) & 0xfU) + << " reduceOpCode=" << ((control >> 12U) & 0xfU) + << " setCkeId=" << TraceSlot(instr.words[3], 0) + << " setCkeMask=" << TraceSlot(instr.words[3], 1) + << " waitCkeId=" << TraceSlot(instr.words[3], 2) + << " waitCkeMask=" << TraceSlot(instr.words[3], 3); + break; + } + case TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_RMT_MEM_HEADER: { + const uint16_t control = TraceSlot(instr.words[1], 3); + const uint16_t flags = TraceSlot(instr.words[2], 3); + std::cerr << "decoded=TransLocMemToRmtMem" + << " localGsa=" << TraceSlot(instr.words[0], 3) + << " localXn=" << TraceSlot(instr.words[1], 0) + << " remoteGsa=" << TraceSlot(instr.words[0], 1) + << " remoteXn=" << TraceSlot(instr.words[0], 2) + << " lengthXn=" << TraceSlot(instr.words[1], 1) + << " channelId=" << TraceSlot(instr.words[1], 2) + << " clearType=" << (flags & 0x1U) + << " lengthEn=" << ((flags >> 1U) & 0x1U) + << " reduceEn=" << ((flags >> 2U) & 0x1U) + << " reduceDataType=" << ((control >> 8U) & 0xfU) + << " reduceOpCode=" << ((control >> 12U) & 0xfU) + << " setCkeId=" << TraceSlot(instr.words[3], 0) + << " setCkeMask=" << TraceSlot(instr.words[3], 1) + << " waitCkeId=" << TraceSlot(instr.words[3], 2) + << " waitCkeMask=" << TraceSlot(instr.words[3], 3); + break; + } + case TILEXR_CCU_TRACE_SYNC_XN_HEADER: + std::cerr << "decoded=SyncXn" + << " remoteXn=" << TraceSlot(instr.words[0], 1) + << " localXn=" << TraceSlot(instr.words[0], 2) + << " channelId=" << TraceSlot(instr.words[1], 0) + << " notifyCke=" << TraceSlot(instr.words[1], 1) + << " notifyMask=" << TraceSlot(instr.words[1], 2) + << " traceFlag=" << std::hex << std::showbase << instr.words[2] + << std::dec << std::noshowbase + << " setCkeId=" << TraceSlot(instr.words[3], 0) + << " setCkeMask=" << TraceSlot(instr.words[3], 1) + << " waitCkeId=" << TraceSlot(instr.words[3], 2) + << " waitCkeMask=" << TraceSlot(instr.words[3], 3); + break; + case TILEXR_CCU_TRACE_SYNC_CKE_HEADER: + std::cerr << "decoded=SyncCke" + << " remoteCke=" << TraceSlot(instr.words[0], 1) + << " localCke=" << TraceSlot(instr.words[0], 2) + << " localCkeMask=" << TraceSlot(instr.words[0], 3) + << " channelId=" << TraceSlot(instr.words[1], 0) + << " clearType=" << TraceSlot(instr.words[2], 3) + << " setCkeId=" << TraceSlot(instr.words[3], 0) + << " setCkeMask=" << TraceSlot(instr.words[3], 1) + << " waitCkeId=" << TraceSlot(instr.words[3], 2) + << " waitCkeMask=" << TraceSlot(instr.words[3], 3); + break; + case TILEXR_CCU_TRACE_SET_CKE_HEADER: + std::cerr << "decoded=SetCke" + << " clearType=" << TraceSlot(instr.words[0], 1) + << " ckeId=" << TraceSlot(instr.words[0], 2) + << " mask=" << TraceSlot(instr.words[0], 3) + << " waitCkeId=" << TraceSlot(instr.words[1], 0) + << " waitMask=" << TraceSlot(instr.words[1], 1); + break; + case TILEXR_CCU_TRACE_CLEAR_CKE_HEADER: + std::cerr << "decoded=ClearCke" + << " clearType=" << TraceSlot(instr.words[0], 1) + << " ckeId=" << TraceSlot(instr.words[0], 2) + << " mask=" << TraceSlot(instr.words[0], 3) + << " waitCkeId=" << TraceSlot(instr.words[1], 0) + << " waitMask=" << TraceSlot(instr.words[1], 1); + break; + default: + std::cerr << "decoded=Unknown opcode=" << std::hex << std::showbase << opcode + << std::dec << std::noshowbase; + break; + } + std::cerr << "\n"; +} + +void TraceInstr(const char* label, size_t index, const TileXRCcuInstr& instr) +{ + std::cerr << "TileXRDirectCcuTrace " << label << "[" << index << "] words=" + << std::hex << std::showbase + << instr.words[0] << "," << instr.words[1] << "," + << instr.words[2] << "," << instr.words[3] + << std::dec << std::noshowbase << "\n"; + TraceDecodedInstr(label, index, instr); +} + +void TraceDirectInstallAttempt(const TileXRCcuDirectInstallAttempt& attempt) +{ + if (!DirectTraceEnabled()) { + return; + } + + std::cerr << "TileXRDirectCcuTrace begin" + << " missionId=" << static_cast(attempt.plan.mission.missionId) + << " missionKey=" << std::hex << std::showbase << attempt.plan.mission.key + << std::dec << std::noshowbase + << " dieId=" << static_cast(attempt.plan.mission.dieId) + << "\n"; + for (size_t i = 0; i < attempt.plan.syncResources.size(); ++i) { + const auto& resource = attempt.plan.syncResources[i]; + std::cerr << "TileXRDirectCcuTrace syncResource[" << i << "]" + << " dieId=" << static_cast(resource.dieId) + << " channelId=" << resource.channelId + << " localXn=" << resource.localXn + << " remoteXn=" << resource.remoteXn + << " notifyCke=" << resource.notifyCke + << " localWaitCke=" << resource.localWaitCke + << " localWaitMask=" << resource.localWaitMask + << " remoteNotifyMask=" << resource.remoteNotifyMask + << " sourceCke=" << resource.sourceCke + << " sourceCkeMask=" << resource.sourceCkeMask + << "\n"; + } + for (size_t i = 0; i < attempt.preparedLowerLayerPlan.remoteXnBindings.size(); ++i) { + const auto& proof = attempt.preparedLowerLayerPlan.remoteXnBindings[i]; + std::cerr << "TileXRDirectCcuTrace remoteXnBinding[" << i << "]" + << " dieId=" << static_cast(proof.dieId) + << " channelId=" << proof.channelId + << " localXn=" << proof.localXn + << " remoteXn=" << proof.remoteXn + << " notifyCke=" << proof.notifyCke + << " localWaitCke=" << proof.localWaitCke + << " peerRank=" << proof.peerRank + << " peerExchangeObserved=" << (proof.peerExchangeObserved ? 1 : 0) + << " endpointRouteVerified=" << (proof.endpointRouteVerified ? 1 : 0) + << " channelResourceOwnerVerified=" << (proof.channelResourceOwnerVerified ? 1 : 0) + << " transportResourceExchangeVerified=" << (proof.transportResourceExchangeVerified ? 1 : 0) + << "\n"; + } + for (size_t i = 0; i < attempt.preparedLowerLayerPlan.pfes.size(); ++i) { + const auto& pfe = attempt.preparedLowerLayerPlan.pfes[i]; + std::cerr << "TileXRDirectCcuTrace lowerLayerPfe[" << i << "]" + << " dieId=" << static_cast(pfe.dieId) + << " pfeOffset=" << pfe.pfeOffset + << " ctx="; + for (uint8_t byte : pfe.ctx.raw) { + std::cerr << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte); + } + std::cerr << std::dec << std::setfill(' ') << "\n"; + TraceDecodedPfeCtx(i, pfe); + } + for (size_t i = 0; i < attempt.preparedLowerLayerPlan.channels.size(); ++i) { + const auto& channel = attempt.preparedLowerLayerPlan.channels[i]; + std::cerr << "TileXRDirectCcuTrace lowerLayerChannel[" << i << "]" + << " dieId=" << static_cast(channel.dieId) + << " channelId=" << channel.channelId + << " ctx="; + for (uint8_t byte : channel.ctx.raw) { + std::cerr << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte); + } + std::cerr << std::dec << std::setfill(' ') << "\n"; + TraceDecodedChannelCtxV1(i, channel); + } + for (size_t i = 0; i < attempt.preparedLowerLayerPlan.jettys.size(); ++i) { + const auto& jetty = attempt.preparedLowerLayerPlan.jettys[i]; + std::cerr << "TileXRDirectCcuTrace lowerLayerJetty[" << i << "]" + << " dieId=" << static_cast(jetty.dieId) + << " startJettyCtxId=" << jetty.startJettyCtxId + << " ctxCount=" << jetty.ctxs.size() + << "\n"; + for (size_t j = 0; j < jetty.ctxs.size(); ++j) { + std::cerr << "TileXRDirectCcuTrace lowerLayerJettyCtx[" << i << "," << j << "] ctx="; + for (uint8_t byte : jetty.ctxs[j].raw) { + std::cerr << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte); + } + std::cerr << std::dec << std::setfill(' ') << "\n"; + TraceDecodedLocalJettyCtx(i, j, jetty.ctxs[j]); + } + } + for (size_t i = 0; i < attempt.plan.taskWindows.size(); ++i) { + const auto& window = attempt.plan.taskWindows[i]; + std::cerr << "TileXRDirectCcuTrace taskWindow[" << i << "]" + << " dieId=" << static_cast(window.dieId) + << " instStartId=" << window.instStartId + << " instCnt=" << window.instCnt + << " argSize=" << window.argSize + << " args="; + for (size_t arg = 0; arg < window.args.size(); ++arg) { + if (arg != 0) { + std::cerr << ","; + } + std::cerr << std::hex << std::showbase << window.args[arg] + << std::dec << std::noshowbase; + } + std::cerr << "\n"; + } + const auto& tracedTasks = attempt.submitTasks.empty() ? attempt.package.tasks : attempt.submitTasks; + for (size_t i = 0; i < tracedTasks.size(); ++i) { + const auto& task = tracedTasks[i]; + std::cerr << "TileXRDirectCcuTrace task[" << i << "]" + << " dieId=" << static_cast(task.dieId) + << " missionId=" << static_cast(task.missionId) + << " timeout=" << task.timeout + << " instStartId=" << task.instStartId + << " instCnt=" << task.instCnt + << " argSize=" << task.argSize + << " key=" << std::hex << std::showbase << task.key + << std::dec << std::noshowbase + << " args="; + for (uint32_t arg = 0; arg < TILEXR_CCU_SQE_ARGS_LEN; ++arg) { + if (arg != 0) { + std::cerr << ","; + } + std::cerr << std::hex << std::showbase << task.args[arg] + << std::dec << std::noshowbase; + } + std::cerr << "\n"; + } + for (size_t i = 0; i < attempt.package.program.sqeLoad.size(); ++i) { + TraceInstr("program.sqeLoad", i, attempt.package.program.sqeLoad[i]); + } + for (size_t i = 0; i < attempt.package.program.sync.size(); ++i) { + TraceInstr("program.sync", i, attempt.package.program.sync[i]); + } + std::cerr << "TileXRDirectCcuTrace end\n"; +} + +void ApplySplitCkeOptions( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuResourceSpec* resourceSpec) +{ + if (resourceSpec == nullptr) { + return; + } + if (options.localWaitCkeStartId != 0 || options.localWaitCkeCount != 0) { + resourceSpec->localWaitCkeStartId = + options.localWaitCkeStartId == 0 ? options.ckeStartId : options.localWaitCkeStartId; + resourceSpec->localWaitCkeCount = + options.localWaitCkeCount == 0 ? resourceSpec->ckeCount : options.localWaitCkeCount; + } + if (options.remoteNotifyCkeStartId != 0 || options.remoteNotifyCkeCount != 0) { + resourceSpec->remoteNotifyCkeStartId = + options.remoteNotifyCkeStartId == 0 ? options.ckeStartId : options.remoteNotifyCkeStartId; + resourceSpec->remoteNotifyCkeCount = + options.remoteNotifyCkeCount == 0 ? resourceSpec->ckeCount : options.remoteNotifyCkeCount; + } +} + +void ApplyRemoteXnOptions( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuResourceSpec* resourceSpec) +{ + if (resourceSpec == nullptr) { + return; + } + if (options.remoteXnStartId != 0 || options.remoteXnCount != 0) { + resourceSpec->remoteXnStartId = options.remoteXnStartId; + resourceSpec->remoteXnCount = + options.remoteXnCount == 0 ? resourceSpec->xnCount : options.remoteXnCount; + } +} + +int PrepareLowerLayerPlanIfNeeded( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (options.prepareLowerLayerPlan == nullptr) { + return TILEXR_SUCCESS; + } + if (attempt == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport planReport; + const int ret = options.prepareLowerLayerPlan( + attempt->allocation, + &plan, + &planReport, + options.lowerLayerPlanUserData); + attempt->lowerLayerPlanReport = planReport; + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = planReport.message.empty() ? + "failed to prepare direct CCU lower-layer install plan" : + planReport.message; + } + return ret; + } + attempt->preparedLowerLayerPlan = plan; + return TILEXR_SUCCESS; +} + +int ReconcileProducerPlanWithLowerLayerProof( + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || attempt->preparedLowerLayerPlan.remoteXnBindings.empty()) { + return TILEXR_SUCCESS; + } + if (attempt->preparedLowerLayerPlan.remoteXnBindings.size() != attempt->plan.syncResources.size()) { + if (report != nullptr) { + report->message = "lower-layer remote XN proof count does not match producer sync resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + for (auto& resource : attempt->plan.syncResources) { + bool matched = false; + for (const auto& proof : attempt->preparedLowerLayerPlan.remoteXnBindings) { + if (proof.dieId != resource.dieId || + proof.channelId != resource.channelId || + proof.localXn != resource.localXn || + !proof.peerExchangeObserved) { + continue; + } + resource.remoteXn = proof.remoteXn; + resource.notifyCke = proof.notifyCke; + if (proof.localWaitCke != 0) { + resource.localWaitCke = proof.localWaitCke; + } + matched = true; + break; + } + if (!matched) { + if (report != nullptr) { + report->message = "lower-layer remote XN proof does not cover producer sync resource"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + return TILEXR_SUCCESS; +} + +int ConfigureDirectMemoryCopyResources( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || attempt->plan.syncResources.size() != 1 || attempt->plan.taskWindows.size() != 1) { + if (report != nullptr) { + report->message = "memory copy direct CCU plan requires one sync resource and one task"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (options.gsaStartId == 0 || attempt->resourceSpec.gsaCount < TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT) { + if (report != nullptr) { + report->message = "memory copy direct CCU requires a kernel-local GSA resource window"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const uint8_t dieId = attempt->specInfo.dieId; + const uint16_t localXnStart = attempt->allocation.localXn.startId; + if (!ContainsRange( + attempt->resourceSpec.xnStartId, + attempt->resourceSpec.xnCount, + localXnStart, + TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT)) { + if (report != nullptr) { + report->message = "memory copy direct CCU local XN window is too small"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + uint16_t remoteXnStart = attempt->allocation.remoteXn.startId; + if (RangesOverlap( + localXnStart, + TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT, + remoteXnStart, + 1U)) { + remoteXnStart = static_cast(localXnStart + TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT); + } + if (!ContainsRange(attempt->resourceSpec.xnStartId, attempt->resourceSpec.xnCount, remoteXnStart, 1U)) { + if (report != nullptr) { + report->message = "memory copy direct CCU remote XN window is outside the XN resource range"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + attempt->allocation.localXn = + MakeRange(dieId, localXnStart, static_cast(TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT)); + attempt->allocation.localGsa = + MakeRange(dieId, options.gsaStartId, static_cast(TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT)); + attempt->allocation.remoteXn = MakeRange(dieId, remoteXnStart, 1U); + attempt->plan.kernelLocalXn = attempt->allocation.localXn; + attempt->plan.kernelLocalGsa = attempt->allocation.localGsa; + attempt->plan.syncResources[0].remoteXn = remoteXnStart; + attempt->plan.taskWindows[0].instCnt = + static_cast(std::max( + attempt->plan.taskWindows[0].instCnt, + TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT)); + return TILEXR_SUCCESS; +} + +int BuildDirectMemoryCopyLaunchPackage( + const TileXRCcuDirectMemoryCopySpec& memoryCopy, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || attempt->plan.syncResources.empty()) { + if (report != nullptr) { + report->message = "missing direct CCU memory copy producer resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const TileXRCcuSyncResource& resource = attempt->plan.syncResources[0]; + if (attempt->plan.kernelLocalGsa.num < TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT || + attempt->plan.kernelLocalXn.num < TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_XN_COUNT) { + if (report != nullptr) { + report->message = "missing direct CCU memory copy GSA/XN resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuMemoryCopySpec copySpec; + copySpec.direction = memoryCopy.direction; + copySpec.localGsa = attempt->plan.kernelLocalGsa.startId; + copySpec.remoteGsa = static_cast(attempt->plan.kernelLocalGsa.startId + 1U); + copySpec.localXn = attempt->plan.kernelLocalXn.startId; + copySpec.remoteXn = static_cast(attempt->plan.kernelLocalXn.startId + 1U); + copySpec.lengthXn = static_cast(attempt->plan.kernelLocalXn.startId + 2U); + copySpec.localAddr = memoryCopy.localAddr; + copySpec.localToken = memoryCopy.localToken; + copySpec.remoteAddr = memoryCopy.remoteAddr; + copySpec.remoteToken = memoryCopy.remoteToken; + copySpec.lengthBytes = memoryCopy.lengthBytes; + copySpec.channelId = resource.channelId; + copySpec.completionCke = resource.localWaitCke == 0 ? resource.notifyCke : resource.localWaitCke; + copySpec.completionMask = resource.localWaitMask == 0 ? 1U : resource.localWaitMask; + + TileXRCcuProgram program; + TileXRCcuMemoryProgramReport memoryReport; + if (TileXRCcuBuildMemoryCopyProgram(copySpec, &program.sync, &memoryReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = memoryReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuRepositoryImage repository; + TileXRCcuRepositoryReport repositoryReport; + if (TileXRCcuBuildRepositoryImage(attempt->plan, program, &repository, &repositoryReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = repositoryReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + std::vector tasks; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildTasks(attempt->plan, &tasks, &planReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = planReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + attempt->package.plan = attempt->plan; + attempt->package.program = program; + attempt->package.repository = repository; + attempt->package.tasks = tasks; + attempt->package.installScope = TileXRCcuLaunchInstallScope {}; + attempt->package.requiresHardwareInstall = true; + return TILEXR_SUCCESS; +} + +void FillReportFromAttempt(const TileXRCcuDirectInstallAttempt& attempt, TileXRCcuDirectInstallReport* report) +{ + if (report == nullptr) { + return; + } + report->pipelineBuilt = true; + report->installAttempted = attempt.installReport.installAttempted; + report->installSucceeded = attempt.installReport.installSucceeded; + report->submitReady = attempt.providerReport.submitReady; + report->requiredInstallSurfaceCount = attempt.installReport.requiredInstallSurfaceCount; + report->publicVerifiedInstallSurfaceCount = attempt.installReport.publicVerifiedInstallSurfaceCount; + report->missingInstallSurfaceCount = attempt.installReport.missingInstallSurfaceCount; + report->taskCount = static_cast(attempt.package.tasks.size()); + report->submitTaskCount = static_cast(attempt.submitTasks.size()); +} + +void ResetSubmitReport(TileXRCcuDirectSubmitReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuDirectSubmitReport{}; + } +} + +void TraceFinalRuntimeTask(size_t taskIndex, const TileXRCcuTask& task) +{ + if (!DirectTraceEnabled()) { + return; + } + std::cerr << "TileXRDirectCcuTrace finalRuntimeTask[" << taskIndex << "]" + << " dieId=" << static_cast(task.dieId) + << " missionId=" << static_cast(task.missionId) + << " timeout=" << task.timeout + << " instStartId=" << task.instStartId + << " instCnt=" << task.instCnt + << " key=0x" << std::hex << std::nouppercase << task.key + << std::dec + << " argSize=" << task.argSize; + for (uint32_t arg = 0; arg < TILEXR_CCU_SQE_ARGS_LEN; ++arg) { + std::cerr << " args[" << arg << "]=0x" + << std::hex << std::nouppercase << task.args[arg] + << std::dec; + } + std::cerr << "\n"; +} + +std::string FormatSubmitTaskFailure( + size_t taskIndex, + int ret, + const TileXRCcuTask& task, + const TileXRCcuRuntimeSubmitReport* runtimeReport) +{ + const TileXRCcuTask& diagnosticTask = + (runtimeReport != nullptr && runtimeReport->finalTaskCaptured) ? + runtimeReport->finalTask : + task; + std::ostringstream oss; + oss << "direct CCU submit failed task=" << taskIndex + << " ret=" << ret; + if (runtimeReport != nullptr && runtimeReport->runtimeLaunchAttempted) { + oss << " rtRet=" << runtimeReport->runtimeRet; + } + oss << " dieId=" << static_cast(diagnosticTask.dieId) + << " missionId=" << static_cast(diagnosticTask.missionId) + << " timeout=" << diagnosticTask.timeout + << " instStartId=" << diagnosticTask.instStartId + << " instCnt=" << diagnosticTask.instCnt + << " key=0x" << std::hex << std::nouppercase << diagnosticTask.key + << std::dec + << " argSize=" << diagnosticTask.argSize; + for (uint32_t arg = 0; arg < TILEXR_CCU_SQE_ARGS_LEN; ++arg) { + oss << " args[" << arg << "]=0x" + << std::hex << std::nouppercase << diagnosticTask.args[arg] + << std::dec; + } + return oss.str(); +} + +int ReturnWithAttemptStatus( + int ret, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt != nullptr) { + FillReportFromAttempt(*attempt, report); + if (report != nullptr) { + if (attempt->providerReport.submitReady) { + report->message = attempt->providerReport.message; + } else if (!attempt->installReport.message.empty()) { + report->message = attempt->installReport.message; + } else { + report->message = attempt->providerReport.message.empty() ? + TILEXR_CCU_DIRECT_KNOWN_MISSING_INSTALL_SURFACES : + attempt->providerReport.message; + } + } + } + return ret; +} + +} // namespace + +int TileXRCcuSubmitPreparedTasks( + const std::vector& submitTasks, + void* stream, + TileXRCcuTaskSubmitFn submitFn, + void* submitUserData, + TileXRCcuDirectSubmitReport* report) +{ + ResetSubmitReport(report); + if (submitTasks.empty()) { + if (report != nullptr) { + report->message = "missing prepared direct CCU submit tasks"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (stream == nullptr) { + if (report != nullptr) { + report->taskCount = static_cast(submitTasks.size()); + report->message = "missing runtime stream for direct CCU submit"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + uint32_t submitted = 0; + for (size_t i = 0; i < submitTasks.size(); ++i) { + TileXRCcuTask taskForSubmit = submitTasks[i]; + + TileXRCcuRuntimeSubmitReport runtimeReport; + const bool useDefaultSubmit = submitFn == nullptr; + const int ret = useDefaultSubmit ? + TileXRCcuSubmitTaskWithReport(taskForSubmit, stream, &runtimeReport) : + submitFn(taskForSubmit, stream, submitUserData); + if (useDefaultSubmit && runtimeReport.finalTaskCaptured) { + TraceFinalRuntimeTask(i, runtimeReport.finalTask); + } + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + report->taskCount = static_cast(submitTasks.size()); + report->submittedTaskCount = submitted; + report->message = FormatSubmitTaskFailure( + i, + ret, + taskForSubmit, + useDefaultSubmit ? &runtimeReport : nullptr); + } + return ret; + } + ++submitted; + } + + if (report != nullptr) { + report->submitted = true; + report->taskCount = static_cast(submitTasks.size()); + report->submittedTaskCount = submitted; + report->message = "direct CCU prepared tasks submitted"; + } + return TILEXR_SUCCESS; +} + +int RunDirectInstallAttemptImpl( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectMemoryCopySpec* memoryCopy, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + ResetReport(report); + if (attempt == nullptr) { + return Fail(nullptr, report, "missing output direct CCU install attempt"); + } + ClearAttempt(attempt); + + if (options.basicInfo == nullptr) { + return Fail(attempt, report, "missing direct CCU basic info"); + } + if (options.provider.empty()) { + return Fail(attempt, report, "missing direct CCU install provider"); + } + if (!options.offlineOnly && !HasRepositoryInstallInputs(options)) { + return Fail(attempt, report, "missing direct CCU repository install inputs"); + } + attempt->repositoryMemoryOps = options.repositoryMemoryOps; + attempt->repositoryMemoryUserData = options.repositoryMemoryUserData; + + TileXRCcuSpecsReport specsReport; + int ret = TileXRCcuDecodeBasicInfo(*options.basicInfo, &attempt->specInfo, &specsReport); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, specsReport.message); + } + + ret = TileXRCcuBuildResourceSpec( + attempt->specInfo, + options.missionStartId, + options.instructionStartId, + options.xnStartId, + options.ckeStartId, + options.channelStartId, + &attempt->resourceSpec, + &specsReport, + options.gsaStartId); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, specsReport.message); + } + attempt->resourceSpec.missionInstructionStartId = options.missionInstructionStartId; + ApplyRemoteXnOptions(options, &attempt->resourceSpec); + ApplySplitCkeOptions(options, &attempt->resourceSpec); + + attempt->resourceRequest.sqeArgCount = memoryCopy == nullptr ? options.sqeArgCount : 0U; + attempt->resourceRequest.syncResourceCount = memoryCopy == nullptr ? options.syncResourceCount : 1U; + attempt->resourceRequest.syncInstructionCount = memoryCopy == nullptr ? + options.syncInstructionCount : + std::max(options.syncInstructionCount, TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT); + attempt->resourceRequest.bindingsPerSyncResource = options.bindingsPerSyncResource; + attempt->resourceRequest.barrierMode = options.barrierMode; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(attempt->resourceSpec) != TILEXR_SUCCESS) { + return Fail(attempt, report, "failed to initialize direct CCU resource allocator"); + } + + TileXRCcuResourceAllocatorReport allocatorReport; + ret = allocator.Allocate( + attempt->resourceRequest, + &attempt->plan, + &attempt->allocation, + &allocatorReport); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, allocatorReport.message); + } + + if (memoryCopy != nullptr) { + ret = ConfigureDirectMemoryCopyResources(options, attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + report == nullptr || report->message.empty() ? + "failed to configure direct CCU memory copy resources" : + report->message); + } + } + + ret = PrepareLowerLayerPlanIfNeeded(options, attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + report == nullptr || report->message.empty() ? + "failed to prepare direct CCU lower-layer install plan" : + report->message); + } + + ret = ReconcileProducerPlanWithLowerLayerProof(attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + report == nullptr || report->message.empty() ? + "failed to reconcile direct CCU lower-layer peer resources" : + report->message); + } + if (memoryCopy == nullptr) { + ret = PopulateHcommStyleSqeTaskArgs(attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, "failed to populate direct CCU SQE task arguments"); + } + } + + TileXRCcuLaunchPackageReport packageReport; + ret = memoryCopy == nullptr ? + TileXRCcuBuildLaunchPackage(attempt->plan, &attempt->package, &packageReport) : + BuildDirectMemoryCopyLaunchPackage(*memoryCopy, attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + memoryCopy == nullptr ? packageReport.message : + (report == nullptr || report->message.empty() ? + "failed to build direct CCU memory copy launch package" : + report->message)); + } + + TraceDirectInstallAttempt(*attempt); + + ret = TileXRCcuBindLaunchPackageInstallScope( + &attempt->package, + options.deviceId, + options.rank, + options.provider); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, "failed to bind direct CCU launch install scope"); + } + + TileXRCcuInstallManifestReport manifestReport; + ret = TileXRCcuBuildInstallManifest(attempt->package, &attempt->manifest, &manifestReport); + if (ret != TILEXR_SUCCESS) { + return Fail(attempt, report, manifestReport.message); + } + + TileXRCcuInstallRequest installRequest; + installRequest.package = &attempt->package; + installRequest.manifest = &attempt->manifest; + installRequest.deviceId = options.deviceId; + installRequest.rank = options.rank; + installRequest.provider = options.provider; + installRequest.offlineOnly = options.offlineOnly; + installRequest.driverAdapter = options.driverAdapter; + installRequest.repositoryMemoryOps = options.repositoryMemoryOps; + installRequest.repositoryMemoryUserData = options.repositoryMemoryUserData; + installRequest.repositoryInstallOptions = options.repositoryInstallOptions; + installRequest.repositoryReceipt = &attempt->repositoryReceipt; + installRequest.installOrder = options.installOrder; + installRequest.lowerLayerPlan = + options.prepareLowerLayerPlan == nullptr ? options.lowerLayerPlan : &attempt->preparedLowerLayerPlan; + + const int installRet = TileXRCcuInstallHardware( + installRequest, + &attempt->evidence, + &attempt->installReport); + + const int submitRet = TileXRCcuPrepareSubmitTasks( + attempt->package, + attempt->evidence, + &attempt->submitTasks, + &attempt->providerReport); + if (submitRet == TILEXR_SUCCESS) { + return ReturnWithAttemptStatus(TILEXR_SUCCESS, attempt, report); + } + if (installRet != TILEXR_SUCCESS) { + return ReturnWithAttemptStatus(installRet, attempt, report); + } + return ReturnWithAttemptStatus(submitRet, attempt, report); +} + +int TileXRCcuRunDirectInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + return RunDirectInstallAttemptImpl(options, nullptr, attempt, report); +} + +int TileXRCcuRunDirectMemoryCopyInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectMemoryCopySpec& memoryCopy, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (memoryCopy.localAddr == 0 || memoryCopy.localToken == 0 || + memoryCopy.remoteAddr == 0 || memoryCopy.remoteToken == 0 || + memoryCopy.lengthBytes == 0) { + ResetReport(report); + ClearAttempt(attempt); + if (report != nullptr) { + report->message = "invalid direct CCU memory copy address/token inputs"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return RunDirectInstallAttemptImpl(options, &memoryCopy, attempt, report); +} + +int TileXRCcuReleaseDirectInstallAttemptResources(TileXRCcuDirectInstallAttempt& attempt) +{ + if (attempt.repositoryReceipt.deviceInstructionPtr == nullptr) { + attempt.repositoryReceipt = TileXRCcuRepositoryInstallReceipt{}; + attempt.repositoryReleaseReport = TileXRCcuRepositoryReport{}; + attempt.repositoryReleaseReport.message = "ok"; + return TILEXR_SUCCESS; + } + return TileXRCcuReleaseRepositoryInstallReceipt( + attempt.repositoryReceipt, + attempt.repositoryMemoryOps, + attempt.repositoryMemoryUserData, + &attempt.repositoryReleaseReport); +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_direct_orchestrator.h b/src/comm/ccu/tilexr_ccu_direct_orchestrator.h new file mode 100644 index 0000000..60530cf --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_orchestrator.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_DIRECT_ORCHESTRATOR_H +#define TILEXR_CCU_DIRECT_ORCHESTRATOR_H + +#include "ccu/tilexr_ccu_install_provider.h" +#include "ccu/tilexr_ccu_lower_layer_plan_builder.h" +#include "ccu/tilexr_ccu_memory_program.h" +#include "ccu/tilexr_ccu_specs.h" + +#include +#include +#include + +namespace TileXR { + +using TileXRCcuLowerLayerPlanPrepareFn = int (*)( + const TileXRCcuResourceAllocation& allocation, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + void* userData); + +struct TileXRCcuDirectInstallOptions { + const TileXRCcuBasicInfo* basicInfo = nullptr; + uint32_t sqeArgCount = 0; + uint32_t syncResourceCount = 0; + uint32_t syncInstructionCount = 0; + uint32_t bindingsPerSyncResource = 1; + uint16_t missionStartId = 0; + uint16_t instructionStartId = 0; + uint16_t missionInstructionStartId = 0; + uint16_t xnStartId = 0; + uint16_t gsaStartId = 0; + uint16_t remoteXnStartId = 0; + uint16_t remoteXnCount = 0; + uint16_t ckeStartId = 0; + uint16_t channelStartId = 0; + uint16_t localWaitCkeStartId = 0; + uint16_t localWaitCkeCount = 0; + uint16_t remoteNotifyCkeStartId = 0; + uint16_t remoteNotifyCkeCount = 0; + TileXRCcuBarrierMode barrierMode = TileXRCcuBarrierMode::SyncXn; + uint32_t deviceId = 0; + uint32_t rank = 0; + std::string provider; + bool offlineOnly = false; + const TileXRCcuDriverAdapter* driverAdapter = nullptr; + TileXRCcuDeviceMemoryOps repositoryMemoryOps; + void* repositoryMemoryUserData = nullptr; + TileXRCcuRepositoryInstallOptions repositoryInstallOptions; + TileXRCcuRepositoryMemoryAllocMode repositoryMemoryAllocMode = TileXRCcuRepositoryMemoryAllocMode::Acl; + TileXRCcuInstallOrder installOrder = TileXRCcuInstallOrder::InstallLowerLayerFirst; + const TileXRCcuLowerLayerInstallPlan* lowerLayerPlan = nullptr; + TileXRCcuLowerLayerPlanPrepareFn prepareLowerLayerPlan = nullptr; + void* lowerLayerPlanUserData = nullptr; +}; + +struct TileXRCcuDirectMemoryCopySpec { + TileXRCcuMemoryCopyDirection direction = TileXRCcuMemoryCopyDirection::RemoteToLocal; + uint64_t localAddr = 0; + uint64_t localToken = 0; + uint64_t remoteAddr = 0; + uint64_t remoteToken = 0; + uint64_t lengthBytes = 0; +}; + +struct TileXRCcuDirectInstallAttempt { + TileXRCcuSpecInfo specInfo; + TileXRCcuResourceSpec resourceSpec; + TileXRCcuResourceRequest resourceRequest; + TileXRCcuResourceAllocation allocation; + TileXRCcuProducerPlan plan; + TileXRCcuLaunchPackage package; + TileXRCcuInstallManifest manifest; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuProviderReport providerReport; + TileXRCcuRepositoryInstallReceipt repositoryReceipt; + TileXRCcuRepositoryReport repositoryReleaseReport; + TileXRCcuDeviceMemoryOps repositoryMemoryOps; + void* repositoryMemoryUserData = nullptr; + TileXRCcuLowerLayerInstallPlan preparedLowerLayerPlan; + TileXRCcuLowerLayerPlanBuilderReport lowerLayerPlanReport; + std::vector submitTasks; +}; + +struct TileXRCcuDirectInstallReport { + bool pipelineBuilt = false; + bool installAttempted = false; + bool installSucceeded = false; + bool submitReady = false; + uint32_t requiredInstallSurfaceCount = 0; + uint32_t publicVerifiedInstallSurfaceCount = 0; + uint32_t missingInstallSurfaceCount = 0; + uint32_t taskCount = 0; + uint32_t submitTaskCount = 0; + std::string message; +}; + +struct TileXRCcuDirectSubmitReport { + bool submitted = false; + uint32_t taskCount = 0; + uint32_t submittedTaskCount = 0; + std::string message; +}; + +using TileXRCcuTaskSubmitFn = int (*)( + const TileXRCcuTask& task, + void* stream, + void* userData); + +int TileXRCcuRunDirectInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report); + +int TileXRCcuRunDirectMemoryCopyInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectMemoryCopySpec& memoryCopy, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report); + +int TileXRCcuReleaseDirectInstallAttemptResources(TileXRCcuDirectInstallAttempt& attempt); + +int TileXRCcuSubmitPreparedTasks( + const std::vector& submitTasks, + void* stream, + TileXRCcuTaskSubmitFn submitFn, + void* submitUserData, + TileXRCcuDirectSubmitReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_DIRECT_ORCHESTRATOR_H diff --git a/src/comm/ccu/tilexr_ccu_direct_runtime.cpp b/src/comm/ccu/tilexr_ccu_direct_runtime.cpp new file mode 100644 index 0000000..c1291d2 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_runtime.cpp @@ -0,0 +1,1529 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_direct_runtime.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr uint32_t TILEXR_CCU_DEFAULT_DIRECT_SQ_DEPTH = 8; +constexpr uint32_t TILEXR_CCU_DIRECT_CCUM_SQE_BYTES = 64; +constexpr uint32_t TILEXR_CCU_DIRECT_SQ_EBB_WORDS = 4; +constexpr uint32_t TILEXR_CCU_DIRECT_LOOP_JETTY_ID = 1024; +constexpr uint32_t TILEXR_CCU_DIRECT_LOOP_JETTY_CTX_ID = 0; +constexpr uint64_t TILEXR_CCU_V1_WQE_BASIC_BLOCK_OFFSET = TILEXR_CCU_V1_CCUM_OFFSET + 0x800000ULL; +constexpr uint64_t TILEXR_CCU_DIRECT_SQ_BUFFER_BYTES = 256ULL * 1024ULL; +constexpr uint32_t TILEXR_CCU_DIRECT_CCU_POLL_CQ_DEPTH = 64; +constexpr uint32_t TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_ASYNC_MAX_POLLS = 1000; +constexpr uint32_t TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_ASYNC_SLEEP_US = 1000; +constexpr uint32_t TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_MAX_ATTEMPTS = 8; +constexpr int TILEXR_CCU_DIRECT_MAX_RANK_SIZE = 128; +constexpr int TILEXR_CCU_HCCP_JFC_MODE_CCU_POLL = 2; +constexpr int TILEXR_CCU_HCCP_ASYNC_EAGAIN = 128301; +constexpr const char* TILEXR_CCU_DIRECT_HDC_TYPE_ENV = "TILEXR_CCU_DIRECT_HDC_TYPE"; +constexpr const char* TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID"; +constexpr const char* TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID"; +constexpr const char* TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE"; +constexpr const char* TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE"; +constexpr const char* TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE_ENV = + "TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE"; +constexpr const char* TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE_ENV = + "TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE"; +constexpr const char* TILEXR_CCU_DIRECT_TRUST_SYNTHETIC_ENDPOINT_ROUTE_ENV = + "TILEXR_CCU_DIRECT_TRUST_SYNTHETIC_ENDPOINT_ROUTE"; +constexpr const char* TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_EXCHANGE_MODE_ENV = + "TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_EXCHANGE_MODE"; +constexpr const char* TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET_ENV = + "TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET"; +constexpr uint64_t TILEXR_CCU_UB_MEM_PAGE_SIZE = 4096ULL; +constexpr uint32_t TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT = 8; + +struct TileXRCcuEndpointTpHandleExchange { + uint64_t tpHandles[TILEXR_CCU_DIRECT_MAX_RANK_SIZE] = {}; + uint32_t psn = 0; +}; + +void ResetReport(TileXRCcuDirectRuntimeReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuDirectRuntimeReport{}; + } +} + +int Fail(TileXRCcuDirectRuntimeReport* report, const std::string& message, int code = TILEXR_ERROR_NOT_FOUND) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +bool IsEmptyEid(const std::array& eid) +{ + return std::all_of(eid.begin(), eid.end(), [](uint8_t value) { return value == 0; }); +} + +std::array ReverseEndpointEid( + const std::array& eid) +{ + std::array reversed {}; + std::reverse_copy(eid.begin(), eid.end(), reversed.begin()); + return reversed; +} + +std::string FormatEndpointEid(const std::array& eid) +{ + std::ostringstream text; + text << std::hex << std::setfill('0'); + for (uint8_t byte : eid) { + text << std::setw(2) << static_cast(byte); + } + return text.str(); +} + +bool HasCompleteEndpointRoute(const TileXRCcuLowerLayerTransportRoute& route) +{ + return !IsEmptyEid(route.remoteEid) && + route.doorbellVa != 0 && + route.doorbellTokenId != 0 && + route.sqDepth != 0; +} + +bool UseImportedPeerEndpointRoute() +{ + const char* mode = std::getenv(TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_EXCHANGE_MODE_ENV); + return mode == nullptr || mode[0] == '\0' || std::strcmp(mode, "imported_peer") == 0; +} + +uint64_t SelectResourceWindowBytes(const TileXRCcuBasicInfo& basicInfo) +{ + (void)basicInfo; + return TILEXR_CCU_RESOURCE_WINDOW_BYTES; +} + +std::string RankEnvName(const char* base, int rank) +{ + std::ostringstream name; + name << base << "_RANK" << rank; + return name.str(); +} + +const char* SelectRankedEnv(const char* base, int rank) +{ + const std::string rankedName = RankEnvName(base, rank); + const char* ranked = std::getenv(rankedName.c_str()); + if (ranked != nullptr && ranked[0] != '\0') { + return ranked; + } + const char* value = std::getenv(base); + return value != nullptr && value[0] != '\0' ? value : nullptr; +} + +bool ParseUnsignedEnv(const char* value, uint64_t* out) +{ + if (value == nullptr || value[0] == '\0' || out == nullptr) { + return false; + } + errno = 0; + char* end = nullptr; + const unsigned long long parsed = std::strtoull(value, &end, 0); + if (errno != 0 || end == value || *end != '\0') { + return false; + } + *out = static_cast(parsed); + return true; +} + +uint64_t SelectRemoteCcuVaOffset() +{ + uint64_t value = 0; + return ParseUnsignedEnv(std::getenv(TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET_ENV), &value) ? value : 0; +} + +bool ParseU32RankedEnv(const char* base, int rank, uint32_t* out) +{ + uint64_t value = 0; + if (!ParseUnsignedEnv(SelectRankedEnv(base, rank), &value) || value > 0xffffffffULL || out == nullptr) { + return false; + } + *out = static_cast(value); + return true; +} + +bool HasRankedEnv(const char* base, int rank) +{ + return SelectRankedEnv(base, rank) != nullptr; +} + +bool IsRaCtxResourceWindowRegistrationMode() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE_ENV); + if (value == nullptr || value[0] == '\0') { + return false; + } + const std::string mode(value); + return mode == "ra_ctx" || mode == "ractx" || mode == "public_ra_ctx" || mode == "1"; +} + +bool TrustSyntheticEndpointRouteForDiagnostics() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_TRUST_SYNTHETIC_ENDPOINT_ROUTE_ENV); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +bool TraceEndpointRoute() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE_ENV); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +void TraceEndpointRouteStep(const std::string& message) +{ + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute " << message << std::endl; + } +} + +bool IsRaCtxLoopEndpointRouteCollectionMode() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE_ENV); + if (value == nullptr || value[0] == '\0') { + return false; + } + const std::string mode(value); + return mode == "ra_ctx_loop" || mode == "ractx_loop" || mode == "public_ra_ctx_loop"; +} + +uint64_t AlignResourceWindowAddr(uint64_t addr) +{ + return addr & ~(TILEXR_CCU_UB_MEM_PAGE_SIZE - 1ULL); +} + +bool HasRaCtxResourceWindowSymbols(const TileXRCcuHccpLoader& loader) +{ + return loader.RaGetDevEidInfoNum != nullptr && + loader.RaGetDevEidInfoList != nullptr && + loader.RaCtxInit != nullptr && + loader.RaCtxDeinit != nullptr && + loader.RaCtxTokenIdAlloc != nullptr && + loader.RaCtxTokenIdFree != nullptr && + loader.RaGetSecRandom != nullptr && + loader.RaCtxLmemRegister != nullptr && + loader.RaCtxLmemUnregister != nullptr; +} + +bool HasRaCtxEndpointRouteSymbols(const TileXRCcuHccpLoader& loader) +{ + return loader.RaCtxCqCreate != nullptr && + loader.RaCtxCqDestroy != nullptr && + loader.RaCtxQpCreate != nullptr && + loader.RaCtxQpDestroy != nullptr && + loader.RaCtxQpImport != nullptr && + loader.RaCtxQpUnimport != nullptr && + loader.RaGetTpInfoListAsync != nullptr && + loader.RaGetAsyncReqResult != nullptr; +} + +uint32_t SelectEndpointRouteSqDepth() +{ + return TILEXR_CCU_DEFAULT_DIRECT_SQ_DEPTH; +} + +uint32_t SelectEndpointRouteSqBytes(uint32_t sqDepth) +{ + return sqDepth * TILEXR_CCU_DIRECT_SQ_EBB_WORDS * TILEXR_CCU_DIRECT_CCUM_SQE_BYTES; +} + +uint64_t SelectEndpointRouteSqVa(const TileXRCcuLocalResourceWindowInfo& localResourceWindow) +{ + return localResourceWindow.addr + TILEXR_CCU_V1_WQE_BASIC_BLOCK_OFFSET + + static_cast(TILEXR_CCU_DIRECT_LOOP_JETTY_CTX_ID) * TILEXR_CCU_DIRECT_SQ_BUFFER_BYTES; +} + +int WaitRaCtxAsyncRequest(TileXRCcuHccpLoader& loader, void* reqHandle) +{ + if (loader.RaGetAsyncReqResult == nullptr || reqHandle == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + for (uint32_t poll = 0; poll < TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_ASYNC_MAX_POLLS; ++poll) { + int reqResult = 0; + const int ret = loader.RaGetAsyncReqResult(reqHandle, &reqResult); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute asyncPoll poll=" << poll + << " ret=" << ret + << " reqResult=" << reqResult << std::endl; + } + if (ret == TILEXR_CCU_HCCP_ASYNC_EAGAIN) { + std::this_thread::sleep_for( + std::chrono::microseconds(TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_ASYNC_SLEEP_US)); + continue; + } + if (ret != 0 || reqResult != 0) { + return TILEXR_ERROR_MKIRT; + } + return TILEXR_SUCCESS; + } + return TILEXR_ERROR_TIMEOUT; +} + +bool ApplyResourceWindowTokenOverride( + int rank, + TileXRCcuLocalResourceWindowInfo* window) +{ + if (window == nullptr || !HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_ENV, rank)) { + return false; + } + uint32_t tokenId = 0; + if (!ParseU32RankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_ENV, rank, &tokenId) || tokenId == 0) { + return false; + } + + uint32_t rawTokenId = tokenId; + if (HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID_ENV, rank) && + !ParseU32RankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID_ENV, rank, &rawTokenId)) { + return false; + } + + uint32_t tokenValue = 0; + if (HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_ENV, rank) && + !ParseU32RankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_ENV, rank, &tokenValue)) { + return false; + } else if (!HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_ENV, rank)) { + tokenValue = window->tokenValue; + } + + window->tokenId = tokenId; + window->rawTokenId = rawTokenId; + window->tokenValue = tokenValue; + return true; +} + +int SelectDirectCcuHdcType() +{ + uint64_t value = 0; + if (!ParseUnsignedEnv(std::getenv(TILEXR_CCU_DIRECT_HDC_TYPE_ENV), &value)) { + return TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2; + } + if (value == TILEXR_CCU_HDC_SERVICE_TYPE_RDMA || value == TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2) { + return static_cast(value); + } + return TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2; +} + +bool ParseHexBytePair(char high, char low, uint8_t* out) +{ + auto hexValue = [](char c) -> int { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; + }; + const int highValue = hexValue(high); + const int lowValue = hexValue(low); + if (highValue < 0 || lowValue < 0 || out == nullptr) { + return false; + } + *out = static_cast((highValue << 4U) | lowValue); + return true; +} + +bool ParseEndpointEid(const char* value, std::array* eid) +{ + if (value == nullptr || eid == nullptr) { + return false; + } + std::string text(value); + if (text.size() >= 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) { + text.erase(0, 2); + } + text.erase(std::remove_if(text.begin(), text.end(), [](char c) { + return c == ':' || c == '-' || c == '_' || c == ' '; + }), text.end()); + if (text.size() != TILEXR_CCU_EID_BYTES * 2U) { + return false; + } + std::array parsed {}; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + if (!ParseHexBytePair(text[i * 2U], text[i * 2U + 1U], &parsed[i])) { + return false; + } + } + *eid = parsed; + return true; +} + +int CollectLocalEndpointRouteFromEnv( + int rank, + TileXRCcuLowerLayerTransportRoute* route, + TileXRCcuDirectRuntimeReport* report) +{ + if (route == nullptr) { + return Fail(report, "missing output direct CCU local endpoint route", TILEXR_ERROR_PARA_CHECK_FAIL); + } + + const char* eidEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID", rank); + const char* tpnEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN", rank); + const char* doorbellVaEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA", rank); + const char* tokenIdEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_ID", rank); + const char* tokenValueEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_VALUE", rank); + const char* sqDepthEnv = SelectRankedEnv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_SQ_DEPTH", rank); + + if (eidEnv == nullptr && tpnEnv == nullptr && doorbellVaEnv == nullptr && + tokenIdEnv == nullptr && tokenValueEnv == nullptr && sqDepthEnv == nullptr) { + return Fail(report, "direct CCU local endpoint route collector is not configured", TILEXR_ERROR_NOT_FOUND); + } + if (eidEnv == nullptr || tpnEnv == nullptr || doorbellVaEnv == nullptr || + tokenIdEnv == nullptr || sqDepthEnv == nullptr) { + return Fail(report, "direct CCU local endpoint route env is incomplete", TILEXR_ERROR_PARA_CHECK_FAIL); + } + + TileXRCcuLowerLayerTransportRoute parsed; + if (!ParseEndpointEid(eidEnv, &parsed.remoteEid)) { + return Fail(report, "invalid direct CCU local endpoint EID env", TILEXR_ERROR_PARA_CHECK_FAIL); + } + uint64_t value = 0; + if (!ParseUnsignedEnv(tpnEnv, &value) || value == 0 || value > 0xffffffffULL) { + return Fail(report, "invalid direct CCU local endpoint TPN env", TILEXR_ERROR_PARA_CHECK_FAIL); + } + parsed.tpn = static_cast(value); + if (!ParseUnsignedEnv(doorbellVaEnv, &parsed.doorbellVa)) { + return Fail(report, "invalid direct CCU local endpoint doorbell VA env", TILEXR_ERROR_PARA_CHECK_FAIL); + } + if (!ParseUnsignedEnv(tokenIdEnv, &value) || value == 0 || value > 0xffffffffULL) { + return Fail(report, "invalid direct CCU local endpoint doorbell token id env", TILEXR_ERROR_PARA_CHECK_FAIL); + } + parsed.doorbellTokenId = static_cast(value); + if (tokenValueEnv != nullptr) { + if (!ParseUnsignedEnv(tokenValueEnv, &value) || value > 0xffffffffULL) { + return Fail(report, "invalid direct CCU local endpoint doorbell token value env", + TILEXR_ERROR_PARA_CHECK_FAIL); + } + parsed.doorbellTokenValue = static_cast(value); + } + if (!ParseUnsignedEnv(sqDepthEnv, &value) || value == 0 || value > 0xffffffffULL) { + return Fail(report, "invalid direct CCU local endpoint SQ depth env", TILEXR_ERROR_PARA_CHECK_FAIL); + } + parsed.sqDepth = static_cast(value); + parsed.endpointRouteVerified = true; + *route = parsed; + return TILEXR_SUCCESS; +} + +void FillProviderResourceWindow( + const TileXRCcuLocalResourceWindowInfo& localResourceWindow, + TileXRCcuEndpointRouteProviderResourceWindow* providerWindow) +{ + if (providerWindow == nullptr) { + return; + } + providerWindow->addr = localResourceWindow.addr; + providerWindow->bytes = localResourceWindow.bytes; + providerWindow->tokenId = localResourceWindow.tokenId; + providerWindow->rawTokenId = localResourceWindow.rawTokenId; + providerWindow->tokenValue = localResourceWindow.tokenValue; +} + +void CopyProviderRoute( + const TileXRCcuEndpointRouteProviderRoute& providerRoute, + TileXRCcuLowerLayerTransportRoute* route) +{ + if (route == nullptr) { + return; + } + TileXRCcuLowerLayerTransportRoute copied; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + copied.remoteEid[i] = providerRoute.remoteEid[i]; + } + copied.tpn = providerRoute.tpn; + copied.doorbellVa = providerRoute.doorbellVa; + copied.doorbellTokenId = providerRoute.doorbellTokenId; + copied.doorbellTokenValue = providerRoute.doorbellTokenValue; + copied.sqDepth = providerRoute.sqDepth; + copied.endpointRouteVerified = providerRoute.endpointRouteVerified; + *route = copied; +} + +} // namespace + +int TileXRCcuDirectRuntime::Init( + const TileXRCcuDirectRuntimeOptions& options, + TileXRCcuDirectRuntimeReport* report) +{ + ResetReport(report); + Shutdown(); + options_ = options; + + TileXRCcuHccpLoaderReport loaderReport; + int ret = loader_.Load(&loaderReport); + if (ret != TILEXR_SUCCESS) { + return Fail(report, loaderReport.message); + } + + ret = ResolveDevicePhyId(&devicePhyId_, report); + if (ret != TILEXR_SUCCESS) { + Shutdown(); + return ret; + } + const int hdcType = SelectDirectCcuHdcType(); + if (report != nullptr) { + report->hdcType = hdcType; + } + TileXRCcuHccpLoaderReport raReport; + ret = loader_.InitRaHdc(devicePhyId_, hdcType, true, &raReport); + if (ret != TILEXR_SUCCESS) { + Shutdown(); + return Fail(report, raReport.message, ret); + } + if (report != nullptr) { + report->raInitialized = true; + } + + TileXRCcuHccpLoaderReport tlvReport; + ret = loader_.InitCcuTlv(devicePhyId_, &tlvReport); + if (ret != TILEXR_SUCCESS) { + Shutdown(); + return Fail(report, tlvReport.message, ret); + } + + initialized_ = true; + if (report != nullptr) { + report->initialized = true; + report->raInitialized = true; + report->ccuTlvInitialized = true; + report->logicDevId = static_cast(options_.devId); + report->devicePhyId = devicePhyId_; + report->hdcType = hdcType; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +void TileXRCcuDirectRuntime::Shutdown() +{ + ReleaseLocalEndpointRoute(); + ReleaseRegisteredResourceWindow(); + loader_.Unload(); + cachedBasicInfo_ = TileXRCcuBasicInfo{}; + localResourceWindow_ = TileXRCcuLocalResourceWindowInfo{}; + localVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute{}; + cachedBasicInfoValid_ = false; + resourceWindowRegistered_ = false; + localVerifiedEndpointRouteValid_ = false; + endpointChanHandle_ = nullptr; + endpointCqHandle_ = nullptr; + endpointQpHandle_ = nullptr; + endpointRemoteQpHandle_ = nullptr; + endpointPeerRemoteQpHandles_.clear(); + endpointQpKey_ = TileXRCcuHccpQpKey{}; + endpointQpKeyValid_ = false; + endpointRouteBound_ = false; + endpointPsn_ = 1; + devicePhyId_ = 0; + initialized_ = false; +} + +bool TileXRCcuDirectRuntime::IsAvailable() const +{ + return initialized_ && loader_.IsLoaded() && loader_.RaCustomChannel != nullptr; +} + +int TileXRCcuDirectRuntime::ResolveDevicePhyId(uint32_t* devicePhyId, TileXRCcuDirectRuntimeReport* report) const +{ + if (devicePhyId == nullptr) { + return Fail(report, "missing output CCU physical device id", TILEXR_ERROR_PARA_CHECK_FAIL); + } + TileXRCcuHccpLoaderReport loaderReport; + const int ret = loader_.ResolveDevicePhyId( + static_cast(options_.devId), + devicePhyId, + &loaderReport); + if (ret != TILEXR_SUCCESS) { + return Fail(report, loaderReport.message.empty() ? + "failed to resolve CCU physical device id" : loaderReport.message, ret); + } + if (report != nullptr) { + report->logicDevId = static_cast(options_.devId); + report->devicePhyId = *devicePhyId; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::CreateDriverAdapter( + TileXRCcuDriverAdapter* adapter, + TileXRCcuDriverAdapterReport* report) +{ + if (!IsAvailable()) { + if (report != nullptr) { + *report = TileXRCcuDriverAdapterReport{}; + report->message = "direct CCU runtime is unavailable"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuRaCustomChannelProviderReport providerReport; + int ret = raCustomChannelProvider_.Init(devicePhyId_, loader_.RaCustomChannel, &providerReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDriverAdapterReport{}; + report->message = providerReport.message; + } + return ret; + } + return raCustomChannelProvider_.CreateAdapter(adapter, report); +} + +int TileXRCcuDirectRuntime::QueryBasicInfo( + uint8_t dieId, + TileXRCcuBasicInfo* basicInfo, + TileXRCcuDriverAdapterReport* report) +{ + if (basicInfo == nullptr) { + if (report != nullptr) { + *report = TileXRCcuDriverAdapterReport{}; + report->message = "missing output CCU basic info"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuDriverAdapter adapter; + int ret = CreateDriverAdapter(&adapter, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + bool enabled = false; + ret = adapter.GetDieEnabled(dieId, &enabled, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + if (!enabled) { + if (report != nullptr) { + report->message = "direct CCU die is not enabled"; + } + return TILEXR_ERROR_NOT_FOUND; + } + ret = adapter.GetBasicInfo(dieId, basicInfo, report); + if (ret == TILEXR_SUCCESS) { + cachedBasicInfo_ = *basicInfo; + cachedBasicInfoValid_ = true; + } + return ret; +} + +int TileXRCcuDirectRuntime::RegisterCcuResourceRmaBuffer(uint64_t resourceAddr) +{ + if (!IsAvailable()) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + ReleaseRegisteredResourceWindow(); + if (!cachedBasicInfoValid_ || resourceAddr == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (cachedBasicInfo_.resourceAddr != 0 && cachedBasicInfo_.resourceAddr != resourceAddr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const uint64_t resourceBytes = SelectResourceWindowBytes(cachedBasicInfo_); + if (IsRaCtxResourceWindowRegistrationMode()) { + return RegisterCcuResourceRmaBufferWithRaCtx(resourceAddr, resourceBytes); + } + + localResourceWindow_ = TileXRCcuLocalResourceWindowInfo{}; + localResourceWindow_.addr = resourceAddr; + localResourceWindow_.bytes = resourceBytes; + localResourceWindow_.tokenId = cachedBasicInfo_.msidToken.tokenId; + localResourceWindow_.rawTokenId = cachedBasicInfo_.msidToken.tokenId; + localResourceWindow_.tokenValue = cachedBasicInfo_.msidToken.tokenValue; + if (HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_ENV, options_.rank) && + !ApplyResourceWindowTokenOverride(options_.rank, &localResourceWindow_)) { + localResourceWindow_ = TileXRCcuLocalResourceWindowInfo{}; + resourceWindowRegistered_ = false; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + resourceWindowRegistered_ = true; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::RegisterCcuResourceRmaBufferWithRaCtx( + uint64_t resourceAddr, + uint64_t resourceBytes) +{ + if (!HasRaCtxResourceWindowSymbols(loader_)) { + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuRaInfo raInfo {}; + raInfo.mode = TILEXR_CCU_NETWORK_OFFLINE; + raInfo.phyId = devicePhyId_; + + uint32_t eidNum = 0; + int ret = loader_.RaGetDevEidInfoNum(raInfo, &eidNum); + if (ret != 0 || eidNum == 0) { + return ret == 0 ? TILEXR_ERROR_NOT_FOUND : TILEXR_ERROR_MKIRT; + } + + std::vector eidInfos(eidNum); + uint32_t queriedEidNum = eidNum; + ret = loader_.RaGetDevEidInfoList(raInfo, eidInfos.data(), &queriedEidNum); + if (ret != 0 || queriedEidNum == 0) { + return ret == 0 ? TILEXR_ERROR_NOT_FOUND : TILEXR_ERROR_MKIRT; + } + + void* ctxHandle = nullptr; + void* tokenIdHandle = nullptr; + void* lmemHandle = nullptr; + + TileXRCcuHccpCtxInitCfg ctxCfg {}; + ctxCfg.mode = TILEXR_CCU_NETWORK_OFFLINE; + ctxCfg.rdma.disabledLiteThread = false; + TileXRCcuHccpCtxInitAttr ctxAttr {}; + ctxAttr.phyId = devicePhyId_; + ctxAttr.ub.eidIndex = eidInfos[0].eidIndex; + ctxAttr.ub.eid = eidInfos[0].eid; + + ret = loader_.RaCtxInit(&ctxCfg, &ctxAttr, &ctxHandle); + if (ret != 0 || ctxHandle == nullptr) { + return TILEXR_ERROR_MKIRT; + } + + TileXRCcuHccpTokenId allocatedToken {}; + ret = loader_.RaCtxTokenIdAlloc(ctxHandle, &allocatedToken, &tokenIdHandle); + if (ret != 0 || tokenIdHandle == nullptr) { + if (loader_.RaCtxDeinit != nullptr) { + (void)loader_.RaCtxDeinit(ctxHandle); + } + return TILEXR_ERROR_MKIRT; + } + + uint32_t tokenValue = 0; + TileXRCcuRaInfo randomInfo {}; + randomInfo.mode = TILEXR_CCU_NETWORK_OFFLINE; + randomInfo.phyId = devicePhyId_; + ret = loader_.RaGetSecRandom(&randomInfo, &tokenValue); + if (ret != 0) { + if (loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree(ctxHandle, tokenIdHandle); + } + if (loader_.RaCtxDeinit != nullptr) { + (void)loader_.RaCtxDeinit(ctxHandle); + } + return TILEXR_ERROR_MKIRT; + } + + const uint64_t alignedAddr = AlignResourceWindowAddr(resourceAddr); + const uint64_t alignedBytes = resourceBytes + (resourceAddr - alignedAddr); + TileXRCcuHccpMrRegInfo mr {}; + mr.in.mem.addr = alignedAddr; + mr.in.mem.size = alignedBytes; + mr.in.ub.flags.value = 0; + mr.in.ub.flags.bs.tokenPolicy = TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT; + mr.in.ub.flags.bs.tokenIdValid = 1; + mr.in.ub.flags.bs.access = TILEXR_CCU_HCCP_MEM_SEG_ACCESS_DEFAULT; + mr.in.ub.flags.bs.nonPin = 1; + mr.in.ub.tokenValue = tokenValue; + mr.in.ub.tokenIdHandle = tokenIdHandle; + + ret = loader_.RaCtxLmemRegister(ctxHandle, &mr, &lmemHandle); + if (ret != 0 || lmemHandle == nullptr) { + if (lmemHandle != nullptr && loader_.RaCtxLmemUnregister != nullptr) { + (void)loader_.RaCtxLmemUnregister(ctxHandle, lmemHandle); + } + if (loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree(ctxHandle, tokenIdHandle); + } + if (loader_.RaCtxDeinit != nullptr) { + (void)loader_.RaCtxDeinit(ctxHandle); + } + return TILEXR_ERROR_MKIRT; + } + + const uint32_t rawTokenId = mr.out.ub.tokenId != 0 ? mr.out.ub.tokenId : allocatedToken.tokenId; + localResourceWindow_ = TileXRCcuLocalResourceWindowInfo{}; + localResourceWindow_.addr = resourceAddr; + localResourceWindow_.bytes = resourceBytes; + localResourceWindow_.rawTokenId = rawTokenId; + localResourceWindow_.tokenId = rawTokenId >> TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT; + localResourceWindow_.tokenValue = tokenValue; + localResourceWindow_.targetSegHandle = mr.out.ub.targetSegHandle; + localResourceWindow_.raCtxHandle = ctxHandle; + localResourceWindow_.tokenIdHandle = tokenIdHandle; + localResourceWindow_.lmemHandle = lmemHandle; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + localResourceWindow_.eid[i] = eidInfos[0].eid.raw[i]; + } + localResourceWindow_.eidIndex = eidInfos[0].eidIndex; + localResourceWindow_.raCtxRegistered = true; + resourceWindowRegistered_ = true; + return TILEXR_SUCCESS; +} + +void TileXRCcuDirectRuntime::ReleaseRegisteredResourceWindow() +{ + ReleaseLocalEndpointRoute(); + if (localResourceWindow_.raCtxRegistered) { + if (localResourceWindow_.lmemHandle != nullptr && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxLmemUnregister != nullptr) { + (void)loader_.RaCtxLmemUnregister(localResourceWindow_.raCtxHandle, localResourceWindow_.lmemHandle); + } + if (localResourceWindow_.tokenIdHandle != nullptr && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree(localResourceWindow_.raCtxHandle, localResourceWindow_.tokenIdHandle); + } + if (localResourceWindow_.raCtxHandle != nullptr && loader_.RaCtxDeinit != nullptr) { + (void)loader_.RaCtxDeinit(localResourceWindow_.raCtxHandle); + } + } + localResourceWindow_ = TileXRCcuLocalResourceWindowInfo{}; + localVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute{}; + resourceWindowRegistered_ = false; + localVerifiedEndpointRouteValid_ = false; +} + +void TileXRCcuDirectRuntime::ReleasePeerEndpointImports() +{ + if (localResourceWindow_.raCtxHandle != nullptr && loader_.RaCtxQpUnimport != nullptr) { + for (void* handle : endpointPeerRemoteQpHandles_) { + if (handle != nullptr) { + (void)loader_.RaCtxQpUnimport(localResourceWindow_.raCtxHandle, handle); + } + } + } + endpointPeerRemoteQpHandles_.clear(); +} + +void TileXRCcuDirectRuntime::ReleaseLocalEndpointRoute() +{ + ReleasePeerEndpointImports(); + if (endpointRouteBound_ && endpointQpHandle_ != nullptr && loader_.RaCtxQpUnbind != nullptr) { + (void)loader_.RaCtxQpUnbind(endpointQpHandle_); + } + if (endpointRemoteQpHandle_ != nullptr && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxQpUnimport != nullptr) { + (void)loader_.RaCtxQpUnimport(localResourceWindow_.raCtxHandle, endpointRemoteQpHandle_); + } + if (endpointQpHandle_ != nullptr && loader_.RaCtxQpDestroy != nullptr) { + (void)loader_.RaCtxQpDestroy(endpointQpHandle_); + } + if (endpointCqHandle_ != nullptr && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxCqDestroy != nullptr) { + (void)loader_.RaCtxCqDestroy(localResourceWindow_.raCtxHandle, endpointCqHandle_); + } + if (endpointChanHandle_ != nullptr && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxChanDestroy != nullptr) { + (void)loader_.RaCtxChanDestroy(localResourceWindow_.raCtxHandle, endpointChanHandle_); + } + endpointChanHandle_ = nullptr; + endpointCqHandle_ = nullptr; + endpointQpHandle_ = nullptr; + endpointRemoteQpHandle_ = nullptr; + endpointQpKey_ = TileXRCcuHccpQpKey{}; + endpointQpKeyValid_ = false; + endpointRouteBound_ = false; + localVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute{}; + localVerifiedEndpointRouteValid_ = false; +} + +int TileXRCcuDirectRuntime::CollectLocalEndpointRouteWithRaCtx(TileXRCcuLowerLayerTransportRoute* route) +{ + if (route == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *route = TileXRCcuLowerLayerTransportRoute{}; + int lastRet = TILEXR_ERROR_NOT_FOUND; + for (uint32_t attempt = 0; attempt < TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_MAX_ATTEMPTS; ++attempt) { + TileXRCcuLowerLayerTransportRoute attemptRoute; + bool asyncWaitFailed = false; + const int ret = CollectLocalEndpointRouteWithRaCtxOnce(&attemptRoute, &asyncWaitFailed); + if (ret == TILEXR_SUCCESS) { + *route = attemptRoute; + return TILEXR_SUCCESS; + } + lastRet = ret; + if (!asyncWaitFailed || attempt + 1 >= TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_MAX_ATTEMPTS) { + return ret; + } + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute retryAfterAsyncFailure" + << " attempt=" << (attempt + 1) + << " ret=" << ret << std::endl; + } + } + return lastRet; +} + +int TileXRCcuDirectRuntime::CollectLocalEndpointRouteWithRaCtxOnce( + TileXRCcuLowerLayerTransportRoute* route, + bool* asyncWaitFailed) +{ + if (route == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (asyncWaitFailed != nullptr) { + *asyncWaitFailed = false; + } + *route = TileXRCcuLowerLayerTransportRoute{}; + if (!localResourceWindow_.raCtxRegistered || + localResourceWindow_.raCtxHandle == nullptr || + localResourceWindow_.tokenIdHandle == nullptr || + IsEmptyEid(localResourceWindow_.eid) || + !HasRaCtxEndpointRouteSymbols(loader_)) { + TraceEndpointRouteStep("raCtxLoop precheck failed"); + return TILEXR_ERROR_NOT_FOUND; + } + + ReleaseLocalEndpointRoute(); + const uint32_t sqDepth = SelectEndpointRouteSqDepth(); + const uint64_t sqVa = SelectEndpointRouteSqVa(localResourceWindow_); + const uint32_t sqBytes = SelectEndpointRouteSqBytes(sqDepth); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute begin" + << " ctx=" << localResourceWindow_.raCtxHandle + << " sqDepth=" << sqDepth + << " sqVa=0x" << std::hex << sqVa + << " sqBytes=0x" << sqBytes + << std::dec << std::endl; + } + + TileXRCcuHccpCqInfo cqInfo {}; + cqInfo.in.chanHandle = nullptr; + cqInfo.in.depth = TILEXR_CCU_DIRECT_CCU_POLL_CQ_DEPTH; + cqInfo.in.ub.userCtx = 0; + cqInfo.in.ub.mode = TILEXR_CCU_HCCP_JFC_MODE_CCU_POLL; + cqInfo.in.ub.ceqn = 0; + cqInfo.in.ub.flag.value = 0; + int ret = loader_.RaCtxCqCreate(localResourceWindow_.raCtxHandle, &cqInfo, &endpointCqHandle_); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute cqCreate ret=" << ret + << " cq=" << endpointCqHandle_ << std::endl; + } + if (ret != 0 || endpointCqHandle_ == nullptr) { + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_MKIRT; + } + + TileXRCcuHccpQpCreateAttr qpAttr {}; + qpAttr.scqHandle = endpointCqHandle_; + qpAttr.rcqHandle = endpointCqHandle_; + qpAttr.srqHandle = endpointCqHandle_; + qpAttr.sqDepth = sqDepth; + qpAttr.rqDepth = TILEXR_CCU_HCCP_RQ_DEPTH_DEFAULT; + qpAttr.transportMode = TILEXR_CCU_HCCP_TRANSPORT_MODE_RM; + qpAttr.ub.mode = static_cast(TILEXR_CCU_HCCP_JETTY_MODE_CCU); + qpAttr.ub.jettyId = TILEXR_CCU_DIRECT_LOOP_JETTY_ID; + qpAttr.ub.tokenIdHandle = localResourceWindow_.tokenIdHandle; + qpAttr.ub.tokenValue = localResourceWindow_.tokenValue; + qpAttr.ub.flag.value = 0; + qpAttr.ub.flag.bs.shareJfr = 1; + qpAttr.ub.jfsFlag.bs.errorSuspend = 1; + qpAttr.ub.priority = 2; + qpAttr.ub.rnrRetry = TILEXR_CCU_HCCP_RNR_RETRY_DEFAULT; + qpAttr.ub.extMode.cstmFlag.value = 0; + qpAttr.ub.extMode.cstmFlag.bs.sqCstm = 1; + qpAttr.ub.extMode.sq.buffVa = sqVa; + qpAttr.ub.extMode.sq.buffSize = sqBytes; + qpAttr.ub.extMode.sqebbNum = sqDepth; + + TileXRCcuHccpQpCreateInfo qpInfo {}; + ret = loader_.RaCtxQpCreate(localResourceWindow_.raCtxHandle, &qpAttr, &qpInfo, &endpointQpHandle_); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute qpCreate ret=" << ret + << " qp=" << endpointQpHandle_ + << " keySize=" << static_cast(qpInfo.key.size) + << " dbAddr=0x" << std::hex << qpInfo.ub.dbAddr + << " dbTokenId=0x" << qpInfo.ub.dbTokenId + << std::dec << std::endl; + } + if (ret != 0 || endpointQpHandle_ == nullptr) { + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_MKIRT; + } + endpointQpKey_ = qpInfo.key; + endpointQpKeyValid_ = qpInfo.key.size != 0; + + TileXRCcuHccpGetTpCfg tpCfg {}; + tpCfg.flag.value = 0; + tpCfg.flag.bs.rtp = 1; + tpCfg.transMode = TILEXR_CCU_HCCP_TRANSPORT_MODE_RM; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + tpCfg.localEid.raw[i] = localResourceWindow_.eid[i]; + tpCfg.peerEid.raw[i] = localResourceWindow_.eid[i]; + } + TileXRCcuHccpTpInfo tpInfo {}; + uint32_t tpInfoNum = 1; + void* reqHandle = nullptr; + ret = loader_.RaGetTpInfoListAsync( + localResourceWindow_.raCtxHandle, + &tpCfg, + &tpInfo, + &tpInfoNum, + &reqHandle); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute getTpInfoAsync ret=" << ret + << " req=" << reqHandle + << " num=" << tpInfoNum + << " tpHandle=0x" << std::hex << tpInfo.tpHandle + << std::dec << std::endl; + } + if (ret != 0 || reqHandle == nullptr || tpInfoNum == 0) { + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_MKIRT; + } + ret = WaitRaCtxAsyncRequest(loader_, reqHandle); + if (ret != TILEXR_SUCCESS) { + ReleaseLocalEndpointRoute(); + if (asyncWaitFailed != nullptr) { + *asyncWaitFailed = true; + } + return ret; + } + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute getTpInfoDone" + << " tpHandle=0x" << std::hex << tpInfo.tpHandle + << std::dec << std::endl; + } + if (tpInfo.tpHandle == 0) { + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuHccpQpImportInfo importInfo {}; + importInfo.in.key = qpInfo.key; + importInfo.in.ub.mode = TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP; + importInfo.in.ub.tokenValue = localResourceWindow_.tokenValue; + importInfo.in.ub.policy = TILEXR_CCU_HCCP_JETTY_GRP_POLICY_RR; + importInfo.in.ub.type = TILEXR_CCU_HCCP_TARGET_TYPE_JETTY; + importInfo.in.ub.flag.value = 0; + importInfo.in.ub.flag.bs.tokenPolicy = TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT; + importInfo.in.ub.expImportCfg.tpHandle = tpInfo.tpHandle; + importInfo.in.ub.expImportCfg.peerTpHandle = tpInfo.tpHandle; + importInfo.in.ub.expImportCfg.txPsn = endpointPsn_; + importInfo.in.ub.expImportCfg.rxPsn = endpointPsn_; + importInfo.in.ub.tpType = TILEXR_CCU_HCCP_TP_TYPE_RTP; + + ret = loader_.RaCtxQpImport(localResourceWindow_.raCtxHandle, &importInfo, &endpointRemoteQpHandle_); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute qpImport ret=" << ret + << " remoteQp=" << endpointRemoteQpHandle_ + << " tpn=0x" << std::hex << importInfo.out.ub.tpn + << std::dec << std::endl; + } + if (ret != 0 || endpointRemoteQpHandle_ == nullptr) { + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_MKIRT; + } + ++endpointPsn_; + + TileXRCcuLowerLayerTransportRoute collected; + collected.remoteEid = localResourceWindow_.eid; + collected.tpn = importInfo.out.ub.tpn; + collected.doorbellVa = qpInfo.ub.dbAddr; + collected.doorbellTokenId = qpInfo.ub.dbTokenId >> TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT; + collected.doorbellTokenValue = localResourceWindow_.tokenValue; + collected.sqDepth = sqDepth; + collected.endpointRouteVerified = true; + if (!HasCompleteEndpointRoute(collected)) { + TraceEndpointRouteStep("collected route incomplete"); + ReleaseLocalEndpointRoute(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute verified" + << " tpn=0x" << std::hex << collected.tpn + << " doorbellVa=0x" << collected.doorbellVa + << " doorbellTokenId=0x" << collected.doorbellTokenId + << std::dec + << " sqDepth=" << collected.sqDepth << std::endl; + } + *route = collected; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::RefreshLocalVerifiedEndpointRoute(TileXRCcuDirectRuntimeReport* report) +{ + ResetReport(report); + localVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute{}; + localVerifiedEndpointRouteValid_ = false; + if (!resourceWindowRegistered_) { + return Fail(report, "direct CCU resource window is not registered for endpoint route collection", + TILEXR_ERROR_NOT_INITIALIZED); + } + + TileXRCcuLowerLayerTransportRoute route; + int ret = TILEXR_ERROR_NOT_FOUND; + if (options_.localEndpointRouteCollector != nullptr) { + ret = options_.localEndpointRouteCollector( + devicePhyId_, + localResourceWindow_, + &route, + options_.localEndpointRouteCollectorUserData); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "direct CCU local endpoint route collector failed", ret); + } + } else if (IsRaCtxLoopEndpointRouteCollectionMode()) { + ret = CollectLocalEndpointRouteWithRaCtx(&route); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "direct CCU RA ctx loop endpoint route collection failed", ret); + } + } else { + TileXRCcuHccpLoaderReport providerReport; + ret = loader_.LoadEndpointRouteProviderFromEnv(&providerReport); + if (ret == TILEXR_SUCCESS && loader_.CollectLocalEndpointRoute != nullptr) { + TileXRCcuEndpointRouteProviderResourceWindow providerWindow; + FillProviderResourceWindow(localResourceWindow_, &providerWindow); + TileXRCcuEndpointRouteProviderRoute providerRoute; + ret = loader_.CollectLocalEndpointRoute(devicePhyId_, &providerWindow, &providerRoute); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "direct CCU endpoint route provider failed", ret); + } + CopyProviderRoute(providerRoute, &route); + } else if (ret == TILEXR_ERROR_NOT_FOUND && !providerReport.endpointRouteProviderConfigured) { + const bool canCollectRaCtxRoute = + IsRaCtxResourceWindowRegistrationMode() && + localResourceWindow_.raCtxRegistered && + HasRaCtxEndpointRouteSymbols(loader_); + if (canCollectRaCtxRoute) { + ret = CollectLocalEndpointRouteWithRaCtx(&route); + if (ret != TILEXR_SUCCESS && ret != TILEXR_ERROR_NOT_FOUND) { + return Fail(report, "direct CCU RA ctx loop endpoint route collection failed", ret); + } + } + if (ret != TILEXR_SUCCESS) { + ret = CollectLocalEndpointRouteFromEnv(options_.rank, &route, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + } else { + return Fail(report, providerReport.message.empty() ? + "direct CCU endpoint route provider failed to load" : providerReport.message, ret); + } + } + + ret = ConfigureLocalVerifiedEndpointRoute(route); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "direct CCU local endpoint route collector returned incomplete route", ret); + } + if (report != nullptr) { + report->initialized = initialized_; + report->logicDevId = static_cast(options_.devId); + report->devicePhyId = devicePhyId_; + report->message = "direct CCU local endpoint route collected"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::ConfigureLocalVerifiedEndpointRoute( + const TileXRCcuLowerLayerTransportRoute& route) +{ + if (!route.endpointRouteVerified || !HasCompleteEndpointRoute(route)) { + localVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute{}; + localVerifiedEndpointRouteValid_ = false; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + localVerifiedEndpointRoute_ = route; + localVerifiedEndpointRouteValid_ = true; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::ExportLocalCcuRmaBuffer(TileXRCcuLocalResourceWindowInfo* info) const +{ + if (info == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *info = TileXRCcuLocalResourceWindowInfo{}; + if (!resourceWindowRegistered_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + *info = localResourceWindow_; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::QueryTpHandleForPeer( + const std::array& peerEid, + uint64_t* tpHandle) +{ + if (tpHandle == nullptr || IsEmptyEid(localResourceWindow_.eid) || IsEmptyEid(peerEid) || + localResourceWindow_.raCtxHandle == nullptr || loader_.RaGetTpInfoListAsync == nullptr || + loader_.RaGetAsyncReqResult == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *tpHandle = 0; + + TileXRCcuHccpGetTpCfg tpCfg {}; + tpCfg.flag.value = 0; + tpCfg.flag.bs.rtp = 1; + tpCfg.transMode = TILEXR_CCU_HCCP_TRANSPORT_MODE_RM; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + tpCfg.localEid.raw[i] = localResourceWindow_.eid[i]; + tpCfg.peerEid.raw[i] = peerEid[i]; + } + + TileXRCcuHccpTpInfo tpInfo {}; + uint32_t tpInfoNum = 1; + void* reqHandle = nullptr; + const int ret = loader_.RaGetTpInfoListAsync( + localResourceWindow_.raCtxHandle, + &tpCfg, + &tpInfo, + &tpInfoNum, + &reqHandle); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute peerTpInfoAsync" + << " ret=" << ret + << " req=" << reqHandle + << " num=" << tpInfoNum + << " tpHandle=0x" << std::hex << tpInfo.tpHandle + << std::dec << std::endl; + } + if (ret != 0 || reqHandle == nullptr || tpInfoNum == 0) { + return TILEXR_ERROR_MKIRT; + } + const int waitRet = WaitRaCtxAsyncRequest(loader_, reqHandle); + if (waitRet != TILEXR_SUCCESS) { + return waitRet; + } + if (tpInfo.tpHandle == 0) { + return TILEXR_ERROR_NOT_FOUND; + } + *tpHandle = tpInfo.tpHandle; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::ImportPeerEndpointRoute( + const TileXRCcuResourceWindowExchange& peerWindow, + uint64_t localTpHandle, + uint64_t peerTpHandle, + uint32_t localPsn, + uint32_t peerPsn, + TileXRCcuLowerLayerTransportRoute* importedRoute) +{ + if (importedRoute == nullptr || peerWindow.qpKey.size == 0 || localTpHandle == 0 || peerTpHandle == 0 || + localResourceWindow_.raCtxHandle == nullptr || loader_.RaCtxQpImport == nullptr || + !localVerifiedEndpointRouteValid_ || !HasCompleteEndpointRoute(localVerifiedEndpointRoute_)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *importedRoute = TileXRCcuLowerLayerTransportRoute{}; + + TileXRCcuHccpQpImportInfo importInfo {}; + importInfo.in.key = peerWindow.qpKey; + importInfo.in.ub.mode = TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP; + importInfo.in.ub.tokenValue = peerWindow.tokenValue; + importInfo.in.ub.policy = TILEXR_CCU_HCCP_JETTY_GRP_POLICY_RR; + importInfo.in.ub.type = TILEXR_CCU_HCCP_TARGET_TYPE_JETTY; + importInfo.in.ub.flag.value = 0; + importInfo.in.ub.flag.bs.tokenPolicy = TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT; + importInfo.in.ub.expImportCfg.tpHandle = localTpHandle; + importInfo.in.ub.expImportCfg.peerTpHandle = peerTpHandle; + importInfo.in.ub.expImportCfg.txPsn = localPsn; + importInfo.in.ub.expImportCfg.rxPsn = peerPsn; + importInfo.in.ub.tpType = TILEXR_CCU_HCCP_TP_TYPE_RTP; + + void* remoteQpHandle = nullptr; + const int ret = loader_.RaCtxQpImport(localResourceWindow_.raCtxHandle, &importInfo, &remoteQpHandle); + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute peerQpImport" + << " ret=" << ret + << " remoteQp=" << remoteQpHandle + << " localTpHandle=0x" << std::hex << localTpHandle + << " peerTpHandle=0x" << peerTpHandle + << " tpn=0x" << importInfo.out.ub.tpn + << std::dec + << " localPsn=" << localPsn + << " peerPsn=" << peerPsn + << " peerEid=" << FormatEndpointEid(peerWindow.remoteEid) + << " channelEid=" << FormatEndpointEid(ReverseEndpointEid(peerWindow.remoteEid)) + << std::endl; + } + if (ret != 0 || remoteQpHandle == nullptr) { + return TILEXR_ERROR_MKIRT; + } + endpointPeerRemoteQpHandles_.push_back(remoteQpHandle); + + importedRoute->remoteEid = ReverseEndpointEid(peerWindow.remoteEid); + importedRoute->tpn = importInfo.out.ub.tpn; + importedRoute->doorbellVa = localVerifiedEndpointRoute_.doorbellVa; + importedRoute->doorbellTokenId = localVerifiedEndpointRoute_.doorbellTokenId; + importedRoute->doorbellTokenValue = localVerifiedEndpointRoute_.doorbellTokenValue; + importedRoute->sqDepth = localVerifiedEndpointRoute_.sqDepth; + importedRoute->endpointRouteVerified = true; + return HasCompleteEndpointRoute(*importedRoute) ? TILEXR_SUCCESS : TILEXR_ERROR_PARA_CHECK_FAIL; +} + +int TileXRCcuDirectRuntime::ExportRemoteCcuRmaBuffers(std::vector* buffers) +{ + if (buffers == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + buffers->clear(); + if (!resourceWindowRegistered_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (options_.rankSize <= 1) { + return TILEXR_SUCCESS; + } + if (options_.rank < 0 || options_.rank >= options_.rankSize || + options_.rankSize > TILEXR_CCU_DIRECT_MAX_RANK_SIZE || + options_.allGather == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + ReleasePeerEndpointImports(); + + TileXRCcuResourceWindowExchange local { + localResourceWindow_.addr, + localResourceWindow_.bytes, + localResourceWindow_.tokenId, + localResourceWindow_.rawTokenId, + localResourceWindow_.tokenValue, + }; + if (localVerifiedEndpointRouteValid_) { + local.remoteEid = localVerifiedEndpointRoute_.remoteEid; + local.tpn = localVerifiedEndpointRoute_.tpn; + local.doorbellVa = localVerifiedEndpointRoute_.doorbellVa; + local.doorbellTokenId = localVerifiedEndpointRoute_.doorbellTokenId; + local.doorbellTokenValue = localVerifiedEndpointRoute_.doorbellTokenValue; + local.sqDepth = localVerifiedEndpointRoute_.sqDepth; + local.qpKey = endpointQpKey_; + local.psn = endpointPsn_; + local.endpointRouteVerified = true; + local.channelResourceOwnerVerified = localVerifiedEndpointRoute_.channelResourceOwnerVerified; + local.transportResourceExchangeVerified = localVerifiedEndpointRoute_.transportResourceExchangeVerified; + } + std::vector all(static_cast(options_.rankSize)); + const int exportRet = options_.allGather( + &local, + sizeof(local), + all.data(), + options_.allGatherUserData); + if (exportRet != TILEXR_SUCCESS) { + return exportRet; + } + + const bool useImportedPeerRoute = UseImportedPeerEndpointRoute(); + const bool canImportPeerRoutes = + useImportedPeerRoute && + endpointQpKeyValid_ && + localVerifiedEndpointRouteValid_ && + HasCompleteEndpointRoute(localVerifiedEndpointRoute_) && + localResourceWindow_.raCtxHandle != nullptr && + loader_.RaCtxQpImport != nullptr && + loader_.RaGetTpInfoListAsync != nullptr && + loader_.RaGetAsyncReqResult != nullptr; + TileXRCcuEndpointTpHandleExchange localTpHandles {}; + std::vector allTpHandles; + bool peerTpHandlesReady = false; + if (canImportPeerRoutes) { + localTpHandles.psn = endpointPsn_; + for (int peer = 0; peer < options_.rankSize; ++peer) { + if (peer == options_.rank) { + continue; + } + const auto& peerWindow = all[peer]; + if (peerWindow.addr == 0 || IsEmptyEid(peerWindow.remoteEid)) { + continue; + } + uint64_t tpHandle = 0; + if (QueryTpHandleForPeer(peerWindow.remoteEid, &tpHandle) == TILEXR_SUCCESS) { + localTpHandles.tpHandles[peer] = tpHandle; + } + } + allTpHandles.resize(static_cast(options_.rankSize)); + const int tpExchangeRet = options_.allGather( + &localTpHandles, + sizeof(localTpHandles), + allTpHandles.data(), + options_.allGatherUserData); + peerTpHandlesReady = tpExchangeRet == TILEXR_SUCCESS; + } + + const uint64_t remoteCcuVaOffset = SelectRemoteCcuVaOffset(); + buffers->reserve(static_cast(options_.rankSize - 1)); + for (int peer = 0; peer < options_.rankSize; ++peer) { + if (peer == options_.rank) { + continue; + } + const auto& peerWindow = all[peer]; + if (peerWindow.addr == 0) { + buffers->clear(); + return TILEXR_ERROR_NOT_FOUND; + } + TileXRCcuRemoteCcuBufferInfo remote; + if (remoteCcuVaOffset > std::numeric_limits::max() - peerWindow.addr) { + buffers->clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + remote.remoteCcuVa = peerWindow.addr + remoteCcuVaOffset; + remote.peerRank = static_cast(peer); + remote.memoryTokenId = peerWindow.tokenId; + remote.rawMemoryTokenId = peerWindow.rawTokenId; + remote.memoryTokenValue = peerWindow.tokenValue; + remote.remoteEid = ReverseEndpointEid(peerWindow.remoteEid); + TileXRCcuLowerLayerTransportRoute importedRoute; + const uint64_t localTpForPeer = peerTpHandlesReady ? + localTpHandles.tpHandles[peer] : 0; + const uint64_t peerTpForLocal = peerTpHandlesReady ? + allTpHandles[static_cast(peer)].tpHandles[options_.rank] : 0; + const bool importedPeerRoute = + peerTpHandlesReady && + peerTpForLocal != 0 && + localTpForPeer != 0 && + ImportPeerEndpointRoute( + peerWindow, + localTpForPeer, + peerTpForLocal, + localTpHandles.psn, + allTpHandles[static_cast(peer)].psn, + &importedRoute) == TILEXR_SUCCESS; + if (importedPeerRoute) { + remote.remoteEid = importedRoute.remoteEid; + remote.tpn = importedRoute.tpn; + remote.doorbellVa = importedRoute.doorbellVa; + remote.doorbellTokenId = importedRoute.doorbellTokenId; + remote.doorbellTokenValue = importedRoute.doorbellTokenValue; + remote.sqDepth = importedRoute.sqDepth; + } else { + remote.tpn = peerWindow.tpn; + remote.doorbellVa = peerWindow.doorbellVa; + remote.doorbellTokenId = peerWindow.doorbellTokenId; + remote.doorbellTokenValue = peerWindow.doorbellTokenValue; + remote.sqDepth = peerWindow.sqDepth; + if (!useImportedPeerRoute && TraceEndpointRoute() && peerWindow.endpointRouteVerified) { + std::cerr << "TileXRDirectCcuTrace endpointRoute usePeerExportedRoute" + << " peerRank=" << peer + << " tpn=0x" << std::hex << peerWindow.tpn + << " peerEid=" << FormatEndpointEid(peerWindow.remoteEid) + << " channelEid=" << FormatEndpointEid(remote.remoteEid) + << " remoteCcuVa=0x" << remote.remoteCcuVa + << " remoteCcuVaBase=0x" << peerWindow.addr + << " remoteCcuVaOffset=0x" << remoteCcuVaOffset + << std::dec << std::endl; + } + } + if (localVerifiedEndpointRouteValid_) { + remote.localDoorbellVa = localVerifiedEndpointRoute_.doorbellVa; + remote.localDoorbellTokenId = localVerifiedEndpointRoute_.doorbellTokenId; + remote.localDoorbellTokenValue = localVerifiedEndpointRoute_.doorbellTokenValue; + remote.localSqDepth = localVerifiedEndpointRoute_.sqDepth; + } + remote.endpointRouteVerified = peerWindow.endpointRouteVerified; + if (importedPeerRoute) { + remote.endpointRouteVerified = true; + } + remote.channelResourceOwnerVerified = peerWindow.channelResourceOwnerVerified; + remote.transportResourceExchangeVerified = peerWindow.transportResourceExchangeVerified; + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute channelRoute" + << " peerRank=" << peer + << " importedPeerRoute=" << (importedPeerRoute ? 1 : 0) + << " peerTpHandlesReady=" << (peerTpHandlesReady ? 1 : 0) + << " localEid=" << FormatEndpointEid(localResourceWindow_.eid) + << " peerEid=" << FormatEndpointEid(peerWindow.remoteEid) + << " selectedRemoteEid=" << FormatEndpointEid(remote.remoteEid) + << " localTpHandle=0x" << std::hex << localTpForPeer + << " peerTpHandle=0x" << peerTpForLocal + << " tpn=0x" << remote.tpn + << " remoteCcuVa=0x" << remote.remoteCcuVa + << " remoteCcuVaBase=0x" << peerWindow.addr + << " remoteCcuVaOffset=0x" << remoteCcuVaOffset + << " doorbellVa=0x" << remote.doorbellVa + << " localDoorbellVa=0x" << remote.localDoorbellVa + << " memoryTokenId=0x" << remote.memoryTokenId + << " rawMemoryTokenId=0x" << remote.rawMemoryTokenId + << " memoryTokenValue=0x" << remote.memoryTokenValue + << std::dec + << " endpointRouteVerified=" << (remote.endpointRouteVerified ? 1 : 0) + << " channelResourceOwnerVerified=" << (remote.channelResourceOwnerVerified ? 1 : 0) + << " transportResourceExchangeVerified=" + << (remote.transportResourceExchangeVerified ? 1 : 0) + << std::endl; + } + buffers->push_back(remote); + } + if (peerTpHandlesReady) { + ++endpointPsn_; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot& templateSnapshot, + TileXRCcuLowerLayerTransportSnapshot* snapshot) const +{ + if (snapshot == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *snapshot = TileXRCcuLowerLayerTransportSnapshot{}; + if (!resourceWindowRegistered_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + + *snapshot = templateSnapshot; + for (uint32_t i = 0; i < snapshot->routes.size(); ++i) { + auto& route = snapshot->routes[i]; + const bool preserveVerifiedRoute = route.endpointRouteVerified && HasCompleteEndpointRoute(route); + bool filledSyntheticEndpointField = false; + if (IsEmptyEid(route.remoteEid)) { + route.remoteEid[0] = static_cast(devicePhyId_ & 0xffU); + route.remoteEid[1] = static_cast((devicePhyId_ >> 8U) & 0xffU); + route.remoteEid[2] = static_cast(route.peerRank & 0xffU); + route.remoteEid[3] = static_cast((route.peerRank >> 8U) & 0xffU); + route.remoteEid[4] = static_cast(options_.rank & 0xff); + route.remoteEid[5] = static_cast(snapshot->dieId); + route.remoteEid[6] = static_cast(route.channelId & 0xffU); + route.remoteEid[7] = static_cast((route.channelId >> 8U) & 0xffU); + filledSyntheticEndpointField = true; + } + if (!preserveVerifiedRoute && route.tpn == 0) { + route.tpn = route.channelId + 1U; + filledSyntheticEndpointField = true; + } + if (route.doorbellVa == 0) { + route.doorbellVa = localResourceWindow_.addr + TILEXR_CCU_V1_XN_RESOURCE_OFFSET + + static_cast(snapshot->xnStartId + i) * TILEXR_CCU_XN_SLOT_BYTES; + filledSyntheticEndpointField = true; + } + if (route.doorbellTokenId == 0) { + route.doorbellTokenId = localResourceWindow_.tokenId; + filledSyntheticEndpointField = true; + } + if (!preserveVerifiedRoute && route.doorbellTokenValue == 0) { + route.doorbellTokenValue = localResourceWindow_.tokenValue; + } + if (route.sqDepth == 0) { + route.sqDepth = TILEXR_CCU_DEFAULT_DIRECT_SQ_DEPTH; + filledSyntheticEndpointField = true; + } + route.endpointRouteVerified = (preserveVerifiedRoute && !filledSyntheticEndpointField) || + (filledSyntheticEndpointField && TrustSyntheticEndpointRouteForDiagnostics() && + HasCompleteEndpointRoute(route)); + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_direct_runtime.h b/src/comm/ccu/tilexr_ccu_direct_runtime.h new file mode 100644 index 0000000..3e358f0 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_runtime.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_DIRECT_RUNTIME_H +#define TILEXR_CCU_DIRECT_RUNTIME_H + +#include "ccu/tilexr_ccu_driver_adapter.h" +#include "ccu/tilexr_ccu_hccp_loader.h" +#include "ccu/tilexr_ccu_lower_layer_plan_builder.h" +#include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + +#include +#include +#include +#include +#include + +namespace TileXR { + +using TileXRCcuDirectAllGatherFn = int (*)( + const void* sendBuf, + size_t sendBytes, + void* recvBuf, + void* userData); + +struct TileXRCcuLocalResourceWindowInfo; + +using TileXRCcuLocalEndpointRouteCollectorFn = int (*)( + uint32_t devicePhyId, + const TileXRCcuLocalResourceWindowInfo& localResourceWindow, + TileXRCcuLowerLayerTransportRoute* route, + void* userData); + +struct TileXRCcuDirectRuntimeOptions { + int rank = 0; + int rankSize = 0; + int devId = 0; + TileXRCcuDirectAllGatherFn allGather = nullptr; + void* allGatherUserData = nullptr; + TileXRCcuLocalEndpointRouteCollectorFn localEndpointRouteCollector = nullptr; + void* localEndpointRouteCollectorUserData = nullptr; +}; + +struct TileXRCcuLocalResourceWindowInfo { + uint64_t addr = 0; + uint64_t bytes = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; + uint64_t targetSegHandle = 0; + void* raCtxHandle = nullptr; + void* tokenIdHandle = nullptr; + void* lmemHandle = nullptr; + std::array eid {}; + uint32_t eidIndex = 0; + bool raCtxRegistered = false; +}; + +struct TileXRCcuResourceWindowExchange { + uint64_t addr = 0; + uint64_t bytes = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; + std::array remoteEid {}; + uint32_t tpn = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + TileXRCcuHccpQpKey qpKey {}; + uint32_t psn = 0; + bool endpointRouteVerified = false; + bool channelResourceOwnerVerified = false; + bool transportResourceExchangeVerified = false; +}; + +struct TileXRCcuDirectRuntimeReport { + bool initialized = false; + bool raInitialized = false; + bool ccuTlvInitialized = false; + uint32_t logicDevId = 0; + uint32_t devicePhyId = 0; + int hdcType = 0; + std::string message; +}; + +class TileXRCcuDirectRuntime { +public: + int Init(const TileXRCcuDirectRuntimeOptions& options, TileXRCcuDirectRuntimeReport* report); + void Shutdown(); + bool IsAvailable() const; + + int QueryBasicInfo(uint8_t dieId, TileXRCcuBasicInfo* basicInfo, TileXRCcuDriverAdapterReport* report); + int CreateDriverAdapter(TileXRCcuDriverAdapter* adapter, TileXRCcuDriverAdapterReport* report); + int RegisterCcuResourceRmaBuffer(uint64_t resourceAddr); + int RefreshLocalVerifiedEndpointRoute(TileXRCcuDirectRuntimeReport* report); + int ConfigureLocalVerifiedEndpointRoute(const TileXRCcuLowerLayerTransportRoute& route); + int ExportLocalCcuRmaBuffer(TileXRCcuLocalResourceWindowInfo* info) const; + int ExportRemoteCcuRmaBuffers(std::vector* buffers); + int ExportLowerLayerTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot& templateSnapshot, + TileXRCcuLowerLayerTransportSnapshot* snapshot) const; + +private: + int ResolveDevicePhyId(uint32_t* devicePhyId, TileXRCcuDirectRuntimeReport* report) const; + int RegisterCcuResourceRmaBufferWithRaCtx(uint64_t resourceAddr, uint64_t resourceBytes); + int CollectLocalEndpointRouteWithRaCtx(TileXRCcuLowerLayerTransportRoute* route); + int CollectLocalEndpointRouteWithRaCtxOnce( + TileXRCcuLowerLayerTransportRoute* route, + bool* asyncWaitFailed); + int QueryTpHandleForPeer( + const std::array& peerEid, + uint64_t* tpHandle); + int ImportPeerEndpointRoute( + const TileXRCcuResourceWindowExchange& peerWindow, + uint64_t localTpHandle, + uint64_t peerTpHandle, + uint32_t localPsn, + uint32_t peerPsn, + TileXRCcuLowerLayerTransportRoute* importedRoute); + void ReleasePeerEndpointImports(); + void ReleaseRegisteredResourceWindow(); + void ReleaseLocalEndpointRoute(); + + TileXRCcuDirectRuntimeOptions options_; + TileXRCcuHccpLoader loader_; + TileXRCcuRaCustomChannelProvider raCustomChannelProvider_; + TileXRCcuBasicInfo cachedBasicInfo_ = {}; + TileXRCcuLocalResourceWindowInfo localResourceWindow_ = {}; + TileXRCcuLowerLayerTransportRoute localVerifiedEndpointRoute_ = {}; + bool cachedBasicInfoValid_ = false; + bool resourceWindowRegistered_ = false; + bool localVerifiedEndpointRouteValid_ = false; + void* endpointChanHandle_ = nullptr; + void* endpointCqHandle_ = nullptr; + void* endpointQpHandle_ = nullptr; + void* endpointRemoteQpHandle_ = nullptr; + std::vector endpointPeerRemoteQpHandles_; + TileXRCcuHccpQpKey endpointQpKey_ = {}; + bool endpointQpKeyValid_ = false; + bool endpointRouteBound_ = false; + uint32_t endpointPsn_ = 1; + uint32_t devicePhyId_ = 0; + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_DIRECT_RUNTIME_H diff --git a/src/comm/ccu/tilexr_ccu_driver_adapter.cpp b/src/comm/ccu/tilexr_ccu_driver_adapter.cpp new file mode 100644 index 0000000..3c2c1bb --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_driver_adapter.h" + +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuDriverAdapterReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRCcuDriverAdapterReport{}; +} + +int Fail(TileXRCcuDriverAdapterReport* report, const std::string& message, int code = TILEXR_ERROR_PARA_CHECK_FAIL) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +void InitRequest(uint8_t dieId, uint32_t opcode, TileXRCcuCustomChannelIn* in) +{ + std::memset(in, 0, sizeof(*in)); + in->op = opcode; + in->offsetStartIdx = 0; + in->data.dataInfo.udieIdx = dieId; +} + +void FillCallReport( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + int driverRet, + int opRet, + const std::string& message, + TileXRCcuDriverAdapterReport* report) +{ + if (report == nullptr) { + return; + } + report->devicePhyId = devicePhyId; + report->dieId = dieId; + report->opcode = opcode; + report->driverRet = driverRet; + report->opRet = opRet; + report->message = message; +} + +std::string CcuCustomChannelFailureMessage( + const char* prefix, + uint32_t opcode, + int driverRet, + int opRet) +{ + std::ostringstream message; + message << prefix + << " op=" << opcode + << " driverRet=" << driverRet + << " opRet=" << opRet; + return message.str(); +} + +template +void CopyPayloadToSlot(const Payload& payload, TileXRCcuDataTypeUnion* slot) +{ + std::memcpy(slot, payload.raw, sizeof(payload.raw)); +} + +bool DirectTraceEnabled() +{ + const char* value = std::getenv("TILEXR_CCU_DIRECT_TRACE"); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +uint64_t LoadWord(const void* data, uint32_t offset, uint32_t bytes) +{ + uint64_t word = 0; + if (offset >= bytes) { + return word; + } + const uint32_t copyBytes = std::min(sizeof(word), bytes - offset); + std::memcpy(&word, static_cast(data) + offset, copyBytes); + return word; +} + +void TraceWords(const char* label, const void* data, uint32_t bytes) +{ + const uint32_t wordCount = (bytes + 7U) / 8U; + std::cerr << "TileXRDirectCcuTrace " << label << "Words=" << wordCount; + for (uint32_t i = 0; i < wordCount; ++i) { + std::cerr << " w" << i << "=" << std::hex << std::showbase + << LoadWord(data, i * 8U, bytes) + << std::dec << std::noshowbase; + } + std::cerr << "\n"; +} + +void TraceCustomChannelRequest( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in) +{ + if (!DirectTraceEnabled()) { + return; + } + + const uint32_t payloadBytes = std::min( + in.data.dataInfo.dataLen == 0 ? TILEXR_CCU_DATA_ARRAY_SLOT_BYTES : in.data.dataInfo.dataLen, + sizeof(in.data.dataInfo.dataArray)); + std::cerr << "TileXRDirectCcuTrace customChannel" + << " devicePhyId=" << devicePhyId + << " op=" << opcode + << " dieId=" << static_cast(dieId) + << " requestDieId=" << in.data.dataInfo.udieIdx + << " offset=" << in.offsetStartIdx + << " dataLen=" << in.data.dataInfo.dataLen + << " arraySize=" << in.data.dataInfo.dataArraySize + << " payloadWords=" << ((payloadBytes + 7U) / 8U) + << "\n"; + TraceWords("customChannel.request", &in, std::min(sizeof(in), 256U)); + TraceWords("customChannel.requestTrailer", &in.offsetStartIdx, sizeof(in.offsetStartIdx) + sizeof(in.op)); + TraceWords("customChannel.payload", in.data.dataInfo.dataArray, payloadBytes); +} + +void TraceCustomChannelReturn( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + int driverRet, + const TileXRCcuCustomChannelOut& out) +{ + if (!DirectTraceEnabled()) { + return; + } + + std::cerr << "TileXRDirectCcuTrace customChannel.return" + << " devicePhyId=" << devicePhyId + << " op=" << opcode + << " dieId=" << static_cast(dieId) + << " driverRet=" << driverRet + << " opRet=" << out.opRet + << " offsetNext=" << out.offsetNextIdx + << "\n"; + TraceWords("customChannel.response", &out, std::min(sizeof(out), 256U)); + TraceWords("customChannel.responseTrailer", &out.offsetNextIdx, sizeof(out.offsetNextIdx) + sizeof(out.opRet)); +} + +} // namespace + +int TileXRCcuDriverAdapter::Init( + uint32_t devicePhyId, + TileXRCcuCustomChannelFn customChannel, + void* userData, + TileXRCcuDriverAdapterReport* report) +{ + ResetReport(report); + if (customChannel == nullptr) { + initialized_ = false; + return Fail(report, "missing CCU custom channel callback"); + } + devicePhyId_ = devicePhyId; + customChannel_ = customChannel; + userData_ = userData; + initialized_ = true; + FillCallReport(devicePhyId_, 0, 0, 0, 0, "ok", report); + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::Call( + uint8_t dieId, + uint32_t opcode, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const +{ + TileXRCcuCustomChannelIn in; + InitRequest(dieId, opcode, &in); + return CallPrepared(dieId, opcode, in, out, report); +} + +int TileXRCcuDriverAdapter::CallPrepared( + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (!initialized_ || customChannel_ == nullptr) { + return Fail(report, "CCU driver adapter is not initialized"); + } + if (out == nullptr) { + return Fail(report, "missing CCU custom channel output"); + } + + std::memset(out, 0, sizeof(*out)); + TraceCustomChannelRequest(devicePhyId_, dieId, opcode, in); + const int driverRet = customChannel_(devicePhyId_, in, out, userData_); + TraceCustomChannelReturn(devicePhyId_, dieId, opcode, driverRet, *out); + FillCallReport(devicePhyId_, dieId, opcode, driverRet, out->opRet, "ok", report); + if (driverRet != 0) { + return Fail( + report, + CcuCustomChannelFailureMessage("CCU custom channel call failed", opcode, driverRet, out->opRet), + TILEXR_ERROR_MKIRT); + } + if (out->opRet != 0) { + return Fail( + report, + CcuCustomChannelFailureMessage("CCU custom channel operation failed", opcode, driverRet, out->opRet), + TILEXR_ERROR_MKIRT); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::GetBasicInfo( + uint8_t dieId, + TileXRCcuBasicInfo* basicInfo, + TileXRCcuDriverAdapterReport* report) const +{ + if (basicInfo == nullptr) { + ResetReport(report); + return Fail(report, "missing output CCU basic info"); + } + *basicInfo = TileXRCcuBasicInfo{}; + + TileXRCcuCustomChannelOut out; + const int ret = Call(dieId, TILEXR_CCU_U_OP_GET_BASIC_INFO, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + const auto& raw = out.data.dataInfo.dataArray[0].baseinfo; + basicInfo->dieId = dieId; + basicInfo->msId = raw.msId; + basicInfo->msidToken.tokenId = raw.tokenId; + basicInfo->msidToken.tokenValue = raw.tokenValue; + basicInfo->msidToken.valid = raw.tokenValid != 0; + basicInfo->missionKey = raw.missionKey; + basicInfo->resourceAddr = raw.resourceAddr; + basicInfo->caps.cap0 = raw.caps.cap0; + basicInfo->caps.cap1 = raw.caps.cap1; + basicInfo->caps.cap2 = raw.caps.cap2; + basicInfo->caps.cap3 = raw.caps.cap3; + basicInfo->caps.cap4 = raw.caps.cap4; + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::GetDieEnabled( + uint8_t dieId, + bool* enabled, + TileXRCcuDriverAdapterReport* report) const +{ + if (enabled == nullptr) { + ResetReport(report); + return Fail(report, "missing output CCU die enabled flag"); + } + *enabled = false; + + TileXRCcuCustomChannelOut out; + const int ret = Call(dieId, TILEXR_CCU_U_OP_GET_DIE_WORKING, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + *enabled = out.data.dataInfo.dataArray[0].dieinfo.enableFlag == TILEXR_CCU_ENABLE_FLAG; + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::ReadInstructions( + uint8_t dieId, + uint16_t instructionStartId, + void* instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (instructions == nullptr) { + return Fail(report, "missing output CCU instruction readback buffer"); + } + if (instructionCount == 0 || instructionCount > TILEXR_CCU_MAX_DATA_ARRAY_SIZE) { + return Fail(report, "invalid CCU instruction readback count"); + } + const uint32_t expectedBytes = instructionCount * TILEXR_CCU_INSTRUCTION_BYTES; + if (instructionBytes != expectedBytes) { + return Fail(report, "CCU instruction readback byte size mismatch"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_GET_INSTRUCTION, &in); + in.offsetStartIdx = instructionStartId; + in.data.dataInfo.dataArraySize = instructionCount; + in.data.dataInfo.dataLen = instructionBytes; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_GET_INSTRUCTION, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + auto* dst = static_cast(instructions); + for (uint32_t i = 0; i < instructionCount; ++i) { + std::memcpy( + dst + i * TILEXR_CCU_INSTRUCTION_BYTES, + out.data.dataInfo.dataArray[i].byte32.raw, + TILEXR_CCU_INSTRUCTION_BYTES); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::InstallInstructions( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const +{ + return InstallInstructionsWithDataLen( + dieId, + instructionStartId, + instructionCount, + deviceInstructionAddr, + instructionBytes, + instructionBytes, + report); +} + +int TileXRCcuDriverAdapter::InstallInstructionsWithDataLen( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + uint32_t customChannelDataLen, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (instructionCount == 0) { + return Fail(report, "missing CCU instruction image"); + } + if (deviceInstructionAddr == 0) { + return Fail(report, "missing device CCU instruction image address"); + } + const uint32_t expectedBytes = static_cast(instructionCount) * TILEXR_CCU_INSTRUCTION_BYTES; + if (instructionBytes == 0 || instructionBytes != expectedBytes) { + return Fail(report, "CCU instruction image byte size mismatch"); + } + if (customChannelDataLen == 0) { + return Fail(report, "missing CCU instruction custom channel data length"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_INSTRUCTION, &in); + in.offsetStartIdx = instructionStartId; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = customChannelDataLen; + in.data.dataInfo.dataArray[0].insinfo.resourceAddr = deviceInstructionAddr; + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_INSTRUCTION, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallMsidToken( + uint8_t dieId, + uint32_t msId, + uint32_t tokenId, + uint32_t tokenValue, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_MSID_TOKEN, &in); + in.offsetStartIdx = 0; + in.data.dataInfo.dataArray[0].baseinfo.msId = msId; + in.data.dataInfo.dataArray[0].baseinfo.tokenId = tokenId; + in.data.dataInfo.dataArray[0].baseinfo.tokenValue = tokenValue; + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_MSID_TOKEN, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallPfeCtx( + uint8_t dieId, + uint32_t pfeOffset, + const TileXRCcuPfeCtx& ctx, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_PFE, &in); + in.offsetStartIdx = pfeOffset; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = TILEXR_CCU_PFE_CTX_BYTES; + std::memcpy(&in.data.dataInfo.dataArray[0], ctx.raw, TILEXR_CCU_PFE_CTX_BYTES); + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_PFE, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallJettyCtx( + uint8_t dieId, + uint16_t startJettyCtxId, + const TileXRCcuLocalJettyCtxData* ctxs, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (ctxs == nullptr) { + return Fail(report, "missing CCU local jetty context payloads"); + } + if (count == 0 || count > TILEXR_CCU_MAX_DATA_ARRAY_SIZE) { + return Fail(report, "invalid CCU local jetty context count"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_JETTY_CTX, &in); + in.offsetStartIdx = startJettyCtxId; + in.data.dataInfo.dataArraySize = count; + in.data.dataInfo.dataLen = count * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES; + for (uint32_t i = 0; i < count; ++i) { + CopyPayloadToSlot(ctxs[i], &in.data.dataInfo.dataArray[i]); + } + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_JETTY_CTX, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallChannelCtxV1( + uint8_t dieId, + uint32_t channelId, + const TileXRCcuChannelCtxDataV1& ctx, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_CHANNEL, &in); + in.offsetStartIdx = channelId; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = TILEXR_CCU_CHANNEL_CTX_V1_BYTES; + std::memcpy(&in.data.dataInfo.dataArray[0], ctx.raw, TILEXR_CCU_CHANNEL_CTX_V1_BYTES); + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_CHANNEL, in, &out, report); +} + +int TileXRCcuDriverAdapter::ClearCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (count == 0) { + return Fail(report, "missing CCU CKE range"); + } + + uint32_t remaining = count; + uint32_t offset = startCkeId; + while (remaining > 0) { + const uint32_t batch = std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_CKE, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * TILEXR_CCU_CKE_SLOT_BYTES; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_SET_CKE, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + remaining -= batch; + offset += batch; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::InstallXnRange( + uint8_t dieId, + uint32_t startXnId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (count == 0) { + return Fail(report, "missing CCU XN range"); + } + + uint32_t remaining = count; + uint32_t offset = startXnId; + while (remaining > 0) { + const uint32_t batch = std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_XN, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * TILEXR_CCU_XN_SLOT_BYTES; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_SET_XN, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + remaining -= batch; + offset += batch; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_driver_adapter.h b/src/comm/ccu/tilexr_ccu_driver_adapter.h new file mode 100644 index 0000000..6553411 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_DRIVER_ADAPTER_H +#define TILEXR_CCU_DRIVER_ADAPTER_H + +#include "ccu/tilexr_ccu_hccp_types.h" +#include "ccu/tilexr_ccu_specs.h" + +#include +#include + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_U_OP_GET_BASIC_INFO = 11; +constexpr uint32_t TILEXR_CCU_U_OP_GET_DIE_WORKING = 15; +constexpr uint32_t TILEXR_CCU_U_OP_GET_INSTRUCTION = 201; +constexpr uint32_t TILEXR_CCU_U_OP_SET_MSID_TOKEN = 53; +constexpr uint32_t TILEXR_CCU_U_OP_SET_INSTRUCTION = 251; +constexpr uint32_t TILEXR_CCU_U_OP_SET_XN = 253; +constexpr uint32_t TILEXR_CCU_U_OP_SET_CKE = 254; +constexpr uint32_t TILEXR_CCU_U_OP_SET_PFE = 255; +constexpr uint32_t TILEXR_CCU_U_OP_SET_CHANNEL = 256; +constexpr uint32_t TILEXR_CCU_U_OP_SET_JETTY_CTX = 257; +constexpr uint32_t TILEXR_CCU_ENABLE_FLAG = 1; +constexpr uint32_t TILEXR_CCU_INSTRUCTION_BYTES = 32; +constexpr uint32_t TILEXR_CCU_DATA_ARRAY_SLOT_BYTES = 64; +constexpr uint32_t TILEXR_CCU_XN_SLOT_BYTES = 8; +constexpr uint32_t TILEXR_CCU_CKE_SLOT_BYTES = 8; +constexpr uint32_t TILEXR_CCU_PFE_CTX_BYTES = 8; +constexpr uint32_t TILEXR_CCU_LOCAL_JETTY_CTX_BYTES = 32; +constexpr uint32_t TILEXR_CCU_CHANNEL_CTX_V1_BYTES = 64; +constexpr uint32_t TILEXR_CCU_MAX_DATA_ARRAY_SIZE = 8; + +struct TileXRCcuPfeCtx { + uint8_t raw[TILEXR_CCU_PFE_CTX_BYTES]; +}; + +struct TileXRCcuLocalJettyCtxData { + uint8_t raw[TILEXR_CCU_LOCAL_JETTY_CTX_BYTES]; +}; + +struct TileXRCcuChannelCtxDataV1 { + uint8_t raw[TILEXR_CCU_CHANNEL_CTX_V1_BYTES]; +}; + +struct TileXRCcuDriverAdapterReport { + uint32_t devicePhyId = 0; + uint8_t dieId = 0; + uint32_t opcode = 0; + int driverRet = 0; + int opRet = 0; + std::string message; +}; + +using TileXRCcuCustomChannelFn = int (*)( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData); + +class TileXRCcuDriverAdapter { +public: + int Init( + uint32_t devicePhyId, + TileXRCcuCustomChannelFn customChannel, + void* userData, + TileXRCcuDriverAdapterReport* report); + + int GetBasicInfo(uint8_t dieId, TileXRCcuBasicInfo* basicInfo, TileXRCcuDriverAdapterReport* report) const; + int GetDieEnabled(uint8_t dieId, bool* enabled, TileXRCcuDriverAdapterReport* report) const; + int ReadInstructions( + uint8_t dieId, + uint16_t instructionStartId, + void* instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const; + int InstallInstructions( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const; + int InstallInstructionsWithDataLen( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + uint32_t customChannelDataLen, + TileXRCcuDriverAdapterReport* report) const; + int InstallMsidToken( + uint8_t dieId, + uint32_t msId, + uint32_t tokenId, + uint32_t tokenValue, + TileXRCcuDriverAdapterReport* report) const; + int InstallPfeCtx( + uint8_t dieId, + uint32_t pfeOffset, + const TileXRCcuPfeCtx& ctx, + TileXRCcuDriverAdapterReport* report) const; + int InstallJettyCtx( + uint8_t dieId, + uint16_t startJettyCtxId, + const TileXRCcuLocalJettyCtxData* ctxs, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + int InstallChannelCtxV1( + uint8_t dieId, + uint32_t channelId, + const TileXRCcuChannelCtxDataV1& ctx, + TileXRCcuDriverAdapterReport* report) const; + int ClearCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + int InstallXnRange( + uint8_t dieId, + uint32_t startXnId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + +private: + int Call(uint8_t dieId, uint32_t opcode, TileXRCcuCustomChannelOut* out, TileXRCcuDriverAdapterReport* report) + const; + int CallPrepared( + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const; + + uint32_t devicePhyId_ = 0; + TileXRCcuCustomChannelFn customChannel_ = nullptr; + void* userData_ = nullptr; + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_DRIVER_ADAPTER_H diff --git a/src/comm/ccu/tilexr_ccu_hccp_loader.cpp b/src/comm/ccu/tilexr_ccu_hccp_loader.cpp new file mode 100644 index 0000000..3f28935 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_hccp_loader.h" + +#include "tilexr_types.h" + +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuHccpLoaderReport{}; + } +} + +int Fail(TileXRCcuHccpLoaderReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_NOT_FOUND; +} + +int FailWithCode(TileXRCcuHccpLoaderReport* report, const std::string& message, int code) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +template +bool LoadSymbol(void* handle, Fn& out, const char* primary, const char* fallback) +{ + out = reinterpret_cast(dlsym(handle, primary)); + if (out == nullptr && fallback != nullptr) { + out = reinterpret_cast(dlsym(handle, fallback)); + } + return out != nullptr; +} + +template +void LoadOptionalSymbol(void* handle, Fn& out, const char* primary, const char* fallback) +{ + if (handle == nullptr) { + out = nullptr; + return; + } + out = reinterpret_cast(dlsym(handle, primary)); + if (out == nullptr && fallback != nullptr) { + out = reinterpret_cast(dlsym(handle, fallback)); + } +} + +using RaHdcKey = std::pair; + +std::mutex g_raHdcMtx; +std::map g_raHdcRefs; +uint32_t g_netServiceRefs = 0; +int g_netServiceHdcType = 0; + +struct CcuTlvSession { + void* handle = nullptr; + uint32_t bufferSize = 0; + uint32_t refs = 0; +}; + +std::mutex g_ccuTlvMtx; +std::map g_ccuTlvSessions; + +bool EnvFlag(const char* name) +{ + const char* value = std::getenv(name); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +std::string RaConfigText(const TileXRCcuRaInitConfig& config) +{ + std::ostringstream text; + text << "phyId=" << config.phyId + << " nicPosition=" << config.nicPosition + << " hdcType=" << config.hdcType + << " enableHdcAsync=" << (config.enableHdcAsync ? 1 : 0); + return text.str(); +} + +} // namespace + +TileXRCcuHccpLoader::~TileXRCcuHccpLoader() +{ + Unload(); +} + +int TileXRCcuHccpLoader::Load(TileXRCcuHccpLoaderReport* report) +{ + ResetReport(report); + if (loaded_) { + if (report != nullptr) { + report->loaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + raHandle_ = dlopen("libra.so", RTLD_NOW); + if (raHandle_ == nullptr) { + return Fail(report, std::string("failed to load libra.so: ") + dlerror()); + } + + if (!LoadSymbol(raHandle_, RaCustomChannel, "RaCustomChannel", "ra_custom_channel")) { + Unload(); + return Fail(report, "missing RaCustomChannel/ra_custom_channel in libra.so"); + } + if (!LoadSymbol(raHandle_, RaInit, "RaInit", nullptr)) { + Unload(); + return Fail(report, "missing RaInit in libra.so"); + } + if (!LoadSymbol(raHandle_, RaDeinit, "RaDeinit", nullptr)) { + Unload(); + return Fail(report, "missing RaDeinit in libra.so"); + } + LoadOptionalSymbol(raHandle_, RaTlvInit, "RaTlvInit", nullptr); + LoadOptionalSymbol(raHandle_, RaTlvRequest, "RaTlvRequest", nullptr); + LoadOptionalSymbol(raHandle_, RaTlvDeinit, "RaTlvDeinit", nullptr); + LoadOptionalSymbol(raHandle_, RaGetDevEidInfoNum, "RaGetDevEidInfoNum", "ra_get_dev_eid_info_num"); + LoadOptionalSymbol(raHandle_, RaGetDevEidInfoList, "RaGetDevEidInfoList", "ra_get_dev_eid_info_list"); + LoadOptionalSymbol(raHandle_, RaCtxInit, "RaCtxInit", "ra_ctx_init"); + LoadOptionalSymbol(raHandle_, RaCtxDeinit, "RaCtxDeinit", "ra_ctx_deinit"); + LoadOptionalSymbol(raHandle_, RaCtxTokenIdAlloc, "RaCtxTokenIdAlloc", "ra_ctx_token_id_alloc"); + LoadOptionalSymbol(raHandle_, RaCtxTokenIdFree, "RaCtxTokenIdFree", "ra_ctx_token_id_free"); + LoadOptionalSymbol(raHandle_, RaCtxLmemRegister, "RaCtxLmemRegister", "ra_ctx_lmem_register"); + LoadOptionalSymbol(raHandle_, RaCtxLmemUnregister, "RaCtxLmemUnregister", "ra_ctx_lmem_unregister"); + LoadOptionalSymbol(raHandle_, RaGetSecRandom, "RaGetSecRandom", "ra_get_sec_random"); + LoadOptionalSymbol(raHandle_, RaCtxChanCreate, "RaCtxChanCreate", "ra_ctx_chan_create"); + LoadOptionalSymbol(raHandle_, RaCtxChanDestroy, "RaCtxChanDestroy", "ra_ctx_chan_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxCqCreate, "RaCtxCqCreate", "ra_ctx_cq_create"); + LoadOptionalSymbol(raHandle_, RaCtxCqDestroy, "RaCtxCqDestroy", "ra_ctx_cq_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxQpCreate, "RaCtxQpCreate", "ra_ctx_qp_create"); + LoadOptionalSymbol(raHandle_, RaCtxQpDestroy, "RaCtxQpDestroy", "ra_ctx_qp_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxQpImport, "RaCtxQpImport", "ra_ctx_qp_import"); + LoadOptionalSymbol(raHandle_, RaCtxQpUnimport, "RaCtxQpUnimport", "ra_ctx_qp_unimport"); + LoadOptionalSymbol(raHandle_, RaCtxQpBind, "RaCtxQpBind", "ra_ctx_qp_bind"); + LoadOptionalSymbol(raHandle_, RaCtxQpUnbind, "RaCtxQpUnbind", "ra_ctx_qp_unbind"); + LoadOptionalSymbol(raHandle_, RaGetTpInfoListAsync, "RaGetTpInfoListAsync", "ra_get_tp_info_list_async"); + LoadOptionalSymbol(raHandle_, RaGetAsyncReqResult, "RaGetAsyncReqResult", "ra_get_async_req_result"); + + runtimeHandle_ = dlopen("libruntime.so", RTLD_NOW); + LoadOptionalSymbol(runtimeHandle_, RtGetDevicePhyIdByIndex, "rtGetDevicePhyIdByIndex", nullptr); + LoadOptionalSymbol(runtimeHandle_, RtOpenNetService, "rtOpenNetService", nullptr); + LoadOptionalSymbol(runtimeHandle_, RtCloseNetService, "rtCloseNetService", nullptr); + + loaded_ = true; + if (report != nullptr) { + report->loaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::LoadEndpointRouteProviderFromEnv(TileXRCcuHccpLoaderReport* report) +{ + ResetReport(report); + if (CollectLocalEndpointRoute != nullptr) { + if (report != nullptr) { + report->endpointRouteProviderLoaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + const char* providerPath = std::getenv("TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER"); + if (providerPath == nullptr || providerPath[0] == '\0') { + return Fail(report, "direct CCU endpoint route provider is not configured"); + } + if (report != nullptr) { + report->endpointRouteProviderConfigured = true; + } + + endpointRouteProviderHandle_ = dlopen(providerPath, RTLD_NOW); + if (endpointRouteProviderHandle_ == nullptr) { + return Fail(report, std::string("failed to load direct CCU endpoint route provider: ") + dlerror()); + } + + if (!LoadSymbol( + endpointRouteProviderHandle_, + CollectLocalEndpointRoute, + "TileXRCcuCollectLocalEndpointRoute", + "tilexr_ccu_collect_local_endpoint_route")) { + dlclose(endpointRouteProviderHandle_); + endpointRouteProviderHandle_ = nullptr; + return Fail(report, "missing TileXRCcuCollectLocalEndpointRoute in direct CCU endpoint route provider"); + } + + if (report != nullptr) { + report->endpointRouteProviderLoaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +void TileXRCcuHccpLoader::Unload() +{ + ReleaseCcuTlv(); + ReleaseRaHdc(); + RaCustomChannel = nullptr; + RtGetDevicePhyIdByIndex = nullptr; + RtOpenNetService = nullptr; + RtCloseNetService = nullptr; + RaInit = nullptr; + RaDeinit = nullptr; + RaTlvInit = nullptr; + RaTlvRequest = nullptr; + RaTlvDeinit = nullptr; + RaGetDevEidInfoNum = nullptr; + RaGetDevEidInfoList = nullptr; + RaCtxInit = nullptr; + RaCtxDeinit = nullptr; + RaCtxTokenIdAlloc = nullptr; + RaCtxTokenIdFree = nullptr; + RaCtxLmemRegister = nullptr; + RaCtxLmemUnregister = nullptr; + RaGetSecRandom = nullptr; + RaCtxChanCreate = nullptr; + RaCtxChanDestroy = nullptr; + RaCtxCqCreate = nullptr; + RaCtxCqDestroy = nullptr; + RaCtxQpCreate = nullptr; + RaCtxQpDestroy = nullptr; + RaCtxQpImport = nullptr; + RaCtxQpUnimport = nullptr; + RaCtxQpBind = nullptr; + RaCtxQpUnbind = nullptr; + RaGetTpInfoListAsync = nullptr; + RaGetAsyncReqResult = nullptr; + CollectLocalEndpointRoute = nullptr; + loaded_ = false; + if (endpointRouteProviderHandle_ != nullptr) { + dlclose(endpointRouteProviderHandle_); + endpointRouteProviderHandle_ = nullptr; + } + if (runtimeHandle_ != nullptr) { + dlclose(runtimeHandle_); + runtimeHandle_ = nullptr; + } + if (raHandle_ != nullptr) { + dlclose(raHandle_); + raHandle_ = nullptr; + } +} + +bool TileXRCcuHccpLoader::IsLoaded() const +{ + return loaded_; +} + +int TileXRCcuHccpLoader::ResolveDevicePhyId( + uint32_t logicDevId, + uint32_t* phyId, + TileXRCcuHccpLoaderReport* report) const +{ + if (report != nullptr) { + report->logicDevId = logicDevId; + report->runtimePhyIdRet = 0; + } + if (phyId == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!loaded_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (RtGetDevicePhyIdByIndex == nullptr) { + if (EnvFlag("TILEXR_CCU_DIRECT_ALLOW_LOGIC_PHY_FALLBACK")) { + *phyId = logicDevId; + if (report != nullptr) { + report->devicePhyId = *phyId; + report->message = "rtGetDevicePhyIdByIndex missing, using logic device id fallback"; + } + return TILEXR_SUCCESS; + } + return FailWithCode(report, "missing rtGetDevicePhyIdByIndex in libruntime.so", TILEXR_ERROR_NOT_FOUND); + } + uint32_t resolvedPhyId = 0; + const int ret = RtGetDevicePhyIdByIndex(logicDevId, &resolvedPhyId); + if (report != nullptr) { + report->runtimePhyIdRet = ret; + } + if (ret != 0) { + return FailWithCode(report, "rtGetDevicePhyIdByIndex failed", TILEXR_ERROR_MKIRT); + } + *phyId = resolvedPhyId; + if (report != nullptr) { + report->devicePhyId = *phyId; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::InitRaHdc( + uint32_t devicePhyId, + int hdcType, + bool enableHdcAsync, + TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + report->devicePhyId = devicePhyId; + report->hdcType = hdcType; + } + if (!loaded_) { + return FailWithCode(report, "CCU HCCP loader is not initialized for RA init", TILEXR_ERROR_NOT_INITIALIZED); + } + if (RaInit == nullptr || RaDeinit == nullptr) { + return FailWithCode(report, "missing RaInit/RaDeinit in libra.so", TILEXR_ERROR_NOT_FOUND); + } + if (raHdcInitialized_) { + std::lock_guard lock(g_raHdcMtx); + const auto it = g_raHdcRefs.find(RaHdcKey(raInitConfig_.phyId, raInitConfig_.hdcType)); + if (report != nullptr) { + report->raInitialized = true; + report->raInitRefCount = it == g_raHdcRefs.end() ? 0U : it->second; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + TileXRCcuRaInitConfig config {}; + config.phyId = devicePhyId; + config.nicPosition = TILEXR_CCU_NETWORK_OFFLINE; + config.hdcType = hdcType; + config.enableHdcAsync = enableHdcAsync; + + const RaHdcKey key(config.phyId, config.hdcType); + std::lock_guard lock(g_raHdcMtx); + int ret = AcquireNetServiceLocked(hdcType, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + uint32_t& refCount = g_raHdcRefs[key]; + if (refCount == 0) { + ret = RaInit(&config); + if (report != nullptr) { + report->raInitRet = ret; + } + if (ret != 0) { + g_raHdcRefs.erase(key); + ReleaseNetServiceLocked(nullptr); + std::ostringstream message; + message << "RaInit failed ret=" << ret << ": " << RaConfigText(config); + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + } + ++refCount; + raInitConfig_ = config; + raHdcInitialized_ = true; + if (report != nullptr) { + report->raInitialized = true; + report->raInitRefCount = refCount; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::InitCcuTlv(uint32_t devicePhyId, TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + report->devicePhyId = devicePhyId; + } + if (!loaded_) { + return FailWithCode(report, "CCU HCCP loader is not initialized for TLV init", + TILEXR_ERROR_NOT_INITIALIZED); + } + if (RaTlvInit == nullptr || RaTlvRequest == nullptr || RaTlvDeinit == nullptr) { + return FailWithCode(report, "missing RaTlvInit/RaTlvRequest/RaTlvDeinit in libra.so", + TILEXR_ERROR_NOT_FOUND); + } + if (ccuTlvInitialized_) { + std::lock_guard lock(g_ccuTlvMtx); + const auto it = g_ccuTlvSessions.find(ccuTlvDevicePhyId_); + if (report != nullptr) { + report->ccuTlvInitialized = true; + report->ccuTlvRefCount = it == g_ccuTlvSessions.end() ? 0U : it->second.refs; + report->ccuTlvBufferSize = it == g_ccuTlvSessions.end() ? 0U : it->second.bufferSize; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + std::lock_guard lock(g_ccuTlvMtx); + CcuTlvSession& session = g_ccuTlvSessions[devicePhyId]; + if (session.refs == 0) { + TileXRCcuTlvInitInfo initInfo {}; + initInfo.version = TILEXR_CCU_TLV_VERSION; + initInfo.phyId = devicePhyId; + initInfo.nicPosition = TILEXR_CCU_NETWORK_OFFLINE; + uint32_t bufferSize = 0; + void* tlvHandle = nullptr; + int ret = RaTlvInit(&initInfo, &bufferSize, &tlvHandle); + if (report != nullptr) { + report->raTlvInitRet = ret; + report->ccuTlvBufferSize = bufferSize; + } + if (ret != 0 || tlvHandle == nullptr) { + g_ccuTlvSessions.erase(devicePhyId); + std::ostringstream message; + message << "RaTlvInit failed ret=" << ret + << ": phyId=" << initInfo.phyId + << " nicPosition=" << initInfo.nicPosition + << " version=" << initInfo.version; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + + TileXRCcuTlvMsg sendMsg {}; + TileXRCcuTlvMsg recvMsg {}; + sendMsg.type = TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT; + ret = RaTlvRequest(tlvHandle, TILEXR_CCU_TLV_MODULE_TYPE_CCU, &sendMsg, &recvMsg); + if (report != nullptr) { + report->raTlvRequestRet = ret; + } + if (ret != 0) { + (void)RaTlvDeinit(tlvHandle); + g_ccuTlvSessions.erase(devicePhyId); + std::ostringstream message; + message << "RaTlvRequest CCU_INIT failed ret=" << ret + << ": phyId=" << devicePhyId + << " moduleType=" << TILEXR_CCU_TLV_MODULE_TYPE_CCU + << " msgType=" << TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + session.handle = tlvHandle; + session.bufferSize = bufferSize; + } + + ++session.refs; + ccuTlvDevicePhyId_ = devicePhyId; + ccuTlvInitialized_ = true; + if (report != nullptr) { + report->ccuTlvInitialized = true; + report->ccuTlvRefCount = session.refs; + report->ccuTlvBufferSize = session.bufferSize; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::AcquireNetServiceLocked(int hdcType, TileXRCcuHccpLoaderReport* report) +{ + if (RtOpenNetService == nullptr || RtCloseNetService == nullptr) { + return FailWithCode(report, + "missing rtOpenNetService/rtCloseNetService in libruntime.so", + TILEXR_ERROR_NOT_FOUND); + } + if (g_netServiceRefs > 0) { + if (g_netServiceHdcType != hdcType) { + std::ostringstream message; + message << "runtime net service already opened for hdcType=" << g_netServiceHdcType + << ", requested hdcType=" << hdcType; + return FailWithCode(report, message.str(), TILEXR_ERROR_PARA_CHECK_FAIL); + } + ++g_netServiceRefs; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return TILEXR_SUCCESS; + } + + std::string extParamText("--hdcType=" + std::to_string(hdcType)); + TileXRCcuRtProcExtParam extParam {}; + extParam.paramInfo = extParamText.c_str(); + extParam.paramLen = extParamText.size(); + TileXRCcuRtNetServiceOpenArgs openArgs {}; + openArgs.extParamList = &extParam; + openArgs.extParamCnt = 1; + const int ret = RtOpenNetService(&openArgs); + if (report != nullptr) { + report->rtOpenNetServiceRet = ret; + } + if (ret != 0) { + std::ostringstream message; + message << "rtOpenNetService failed ret=" << ret << ": " << extParamText; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + + g_netServiceHdcType = hdcType; + g_netServiceRefs = 1; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return TILEXR_SUCCESS; +} + +void TileXRCcuHccpLoader::ReleaseNetServiceLocked(TileXRCcuHccpLoaderReport* report) +{ + if (g_netServiceRefs == 0) { + return; + } + if (g_netServiceRefs > 1U) { + --g_netServiceRefs; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return; + } + + int ret = 0; + if (RtCloseNetService != nullptr) { + ret = RtCloseNetService(); + } + if (report != nullptr) { + report->rtCloseNetServiceRet = ret; + report->netServiceRefCount = 0; + } + g_netServiceRefs = 0; + g_netServiceHdcType = 0; +} + +void TileXRCcuHccpLoader::ReleaseCcuTlv() +{ + if (!ccuTlvInitialized_) { + return; + } + std::lock_guard lock(g_ccuTlvMtx); + auto it = g_ccuTlvSessions.find(ccuTlvDevicePhyId_); + if (it != g_ccuTlvSessions.end() && it->second.refs > 1U) { + --it->second.refs; + ccuTlvInitialized_ = false; + ccuTlvDevicePhyId_ = 0; + return; + } + if (it != g_ccuTlvSessions.end()) { + if (RaTlvRequest != nullptr && it->second.handle != nullptr) { + TileXRCcuTlvMsg sendMsg {}; + TileXRCcuTlvMsg recvMsg {}; + sendMsg.type = TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT; + (void)RaTlvRequest(it->second.handle, TILEXR_CCU_TLV_MODULE_TYPE_CCU, &sendMsg, &recvMsg); + } + if (RaTlvDeinit != nullptr && it->second.handle != nullptr) { + (void)RaTlvDeinit(it->second.handle); + } + g_ccuTlvSessions.erase(it); + } + ccuTlvInitialized_ = false; + ccuTlvDevicePhyId_ = 0; +} + +void TileXRCcuHccpLoader::ReleaseRaHdc() +{ + if (!raHdcInitialized_) { + return; + } + const RaHdcKey key(raInitConfig_.phyId, raInitConfig_.hdcType); + std::lock_guard lock(g_raHdcMtx); + auto it = g_raHdcRefs.find(key); + if (it != g_raHdcRefs.end() && it->second > 1U) { + --it->second; + ReleaseNetServiceLocked(nullptr); + raHdcInitialized_ = false; + raInitConfig_ = TileXRCcuRaInitConfig {}; + return; + } + if (RaDeinit != nullptr) { + (void)RaDeinit(&raInitConfig_); + } + if (it != g_raHdcRefs.end()) { + g_raHdcRefs.erase(it); + } + ReleaseNetServiceLocked(nullptr); + raHdcInitialized_ = false; + raInitConfig_ = TileXRCcuRaInitConfig {}; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_hccp_loader.h b/src/comm/ccu/tilexr_ccu_hccp_loader.h new file mode 100644 index 0000000..93ea134 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_HCCP_LOADER_H +#define TILEXR_CCU_HCCP_LOADER_H + +#include "ccu/tilexr_ccu_hccp_types.h" +#include "tilexr_types.h" + +#include + +namespace TileXR { + +struct TileXRCcuHccpLoaderReport { + bool loaded = false; + bool endpointRouteProviderConfigured = false; + bool endpointRouteProviderLoaded = false; + bool raInitialized = false; + bool ccuTlvInitialized = false; + uint32_t logicDevId = 0; + uint32_t devicePhyId = 0; + uint32_t raInitRefCount = 0; + uint32_t netServiceRefCount = 0; + uint32_t ccuTlvRefCount = 0; + uint32_t ccuTlvBufferSize = 0; + int hdcType = 0; + int runtimePhyIdRet = 0; + int rtOpenNetServiceRet = 0; + int rtCloseNetServiceRet = 0; + int raInitRet = 0; + int raDeinitRet = 0; + int raTlvInitRet = 0; + int raTlvRequestRet = 0; + int raTlvDeinitRet = 0; + std::string message; +}; + +class TileXRCcuHccpLoader { +public: + TileXRCcuHccpLoader() = default; + ~TileXRCcuHccpLoader(); + TileXRCcuHccpLoader(const TileXRCcuHccpLoader&) = delete; + TileXRCcuHccpLoader& operator=(const TileXRCcuHccpLoader&) = delete; + + int Load(TileXRCcuHccpLoaderReport* report); + int LoadEndpointRouteProviderFromEnv(TileXRCcuHccpLoaderReport* report); + int InitRaHdc( + uint32_t devicePhyId, + int hdcType, + bool enableHdcAsync, + TileXRCcuHccpLoaderReport* report); + int InitCcuTlv(uint32_t devicePhyId, TileXRCcuHccpLoaderReport* report); + void Unload(); + bool IsLoaded() const; + int ResolveDevicePhyId(uint32_t logicDevId, uint32_t* phyId, TileXRCcuHccpLoaderReport* report = nullptr) const; + + TileXRCcuRaCustomChannelFunc RaCustomChannel = nullptr; + TileXRCcuRtGetDevicePhyIdByIndexFunc RtGetDevicePhyIdByIndex = nullptr; + TileXRCcuRtOpenNetServiceFunc RtOpenNetService = nullptr; + TileXRCcuRtCloseNetServiceFunc RtCloseNetService = nullptr; + TileXRCcuRaInitFunc RaInit = nullptr; + TileXRCcuRaDeinitFunc RaDeinit = nullptr; + TileXRCcuRaTlvInitFunc RaTlvInit = nullptr; + TileXRCcuRaTlvRequestFunc RaTlvRequest = nullptr; + TileXRCcuRaTlvDeinitFunc RaTlvDeinit = nullptr; + TileXRCcuRaGetDevEidInfoNumFunc RaGetDevEidInfoNum = nullptr; + TileXRCcuRaGetDevEidInfoListFunc RaGetDevEidInfoList = nullptr; + TileXRCcuRaCtxInitFunc RaCtxInit = nullptr; + TileXRCcuRaCtxDeinitFunc RaCtxDeinit = nullptr; + TileXRCcuRaCtxTokenIdAllocFunc RaCtxTokenIdAlloc = nullptr; + TileXRCcuRaCtxTokenIdFreeFunc RaCtxTokenIdFree = nullptr; + TileXRCcuRaCtxLmemRegisterFunc RaCtxLmemRegister = nullptr; + TileXRCcuRaCtxLmemUnregisterFunc RaCtxLmemUnregister = nullptr; + TileXRCcuRaGetSecRandomFunc RaGetSecRandom = nullptr; + TileXRCcuRaCtxChanCreateFunc RaCtxChanCreate = nullptr; + TileXRCcuRaCtxChanDestroyFunc RaCtxChanDestroy = nullptr; + TileXRCcuRaCtxCqCreateFunc RaCtxCqCreate = nullptr; + TileXRCcuRaCtxCqDestroyFunc RaCtxCqDestroy = nullptr; + TileXRCcuRaCtxQpCreateFunc RaCtxQpCreate = nullptr; + TileXRCcuRaCtxQpDestroyFunc RaCtxQpDestroy = nullptr; + TileXRCcuRaCtxQpImportFunc RaCtxQpImport = nullptr; + TileXRCcuRaCtxQpUnimportFunc RaCtxQpUnimport = nullptr; + TileXRCcuRaCtxQpBindFunc RaCtxQpBind = nullptr; + TileXRCcuRaCtxQpUnbindFunc RaCtxQpUnbind = nullptr; + TileXRCcuRaGetTpInfoListAsyncFunc RaGetTpInfoListAsync = nullptr; + TileXRCcuRaGetAsyncReqResultFunc RaGetAsyncReqResult = nullptr; + TileXRCcuEndpointRouteProviderFunc CollectLocalEndpointRoute = nullptr; + +private: + void ReleaseCcuTlv(); + void ReleaseRaHdc(); + int AcquireNetServiceLocked(int hdcType, TileXRCcuHccpLoaderReport* report); + void ReleaseNetServiceLocked(TileXRCcuHccpLoaderReport* report = nullptr); + + void* raHandle_ = nullptr; + void* runtimeHandle_ = nullptr; + void* endpointRouteProviderHandle_ = nullptr; + TileXRCcuRaInitConfig raInitConfig_ = {}; + uint32_t ccuTlvDevicePhyId_ = 0; + bool raHdcInitialized_ = false; + bool ccuTlvInitialized_ = false; + bool loaded_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_HCCP_LOADER_H diff --git a/src/comm/ccu/tilexr_ccu_hccp_types.h b/src/comm/ccu/tilexr_ccu_hccp_types.h new file mode 100644 index 0000000..abc75b1 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_types.h @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_HCCP_TYPES_H +#define TILEXR_CCU_HCCP_TYPES_H + +#include "ccu/tilexr_ccu_abi_constants.h" + +#include + +namespace TileXR { + +constexpr int TILEXR_CCU_NETWORK_OFFLINE = 1; +constexpr int TILEXR_CCU_HDC_SERVICE_TYPE_RDMA = 6; +constexpr int TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2 = 18; +constexpr uint32_t TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE = 2048; +constexpr int TILEXR_CCU_TLV_VERSION = 1; +constexpr uint32_t TILEXR_CCU_TLV_MODULE_TYPE_CCU = 1; +constexpr uint32_t TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT = 0; +constexpr uint32_t TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT = 1; +constexpr uint32_t TILEXR_CCU_HCCP_DEV_EID_INFO_NAME_BYTES = 64; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_KEY_BYTES = 128; +constexpr uint32_t TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT = 1; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_READ = 1U << 1U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_WRITE = 1U << 2U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_ATOMIC = 1U << 3U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_DEFAULT = + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_READ | + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_WRITE | + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_ATOMIC; +constexpr uint32_t TILEXR_CCU_HCCP_QP_KEY_BYTES = 64; +constexpr uint32_t TILEXR_CCU_HCCP_CQ_DEPTH_DEFAULT = 16384; +constexpr uint32_t TILEXR_CCU_HCCP_RQ_DEPTH_DEFAULT = 256; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_MODE_CCU = 2; +constexpr uint32_t TILEXR_CCU_HCCP_TRANSPORT_MODE_RM = 1; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP = 1; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_GRP_POLICY_RR = 0; +constexpr uint32_t TILEXR_CCU_HCCP_TARGET_TYPE_JETTY = 1; +constexpr uint32_t TILEXR_CCU_HCCP_TP_TYPE_RTP = 0; +constexpr uint32_t TILEXR_CCU_HCCP_TP_TYPE_CTP = 1; +constexpr uint8_t TILEXR_CCU_HCCP_RNR_RETRY_DEFAULT = 7; + +struct TileXRCcuDataByte8 { + char raw[8]; +}; + +struct TileXRCcuDataByte32 { + char raw[32]; +}; + +struct TileXRCcuDataByte64 { + char raw[64]; +}; + +struct TileXRCcuCustomChannelCaps { + uint32_t cap0; + uint32_t cap1; + uint32_t cap2; + uint32_t cap3; + uint32_t cap4; +}; + +struct TileXRCcuInstrInfo { + uint64_t resourceAddr; +}; + +struct TileXRCcuDieInfo { + uint32_t enableFlag; +}; + +struct TileXRCcuBaseInfoData { + uint32_t msId; + uint32_t tokenId; + uint32_t tokenValue; + uint32_t tokenValid; + uint32_t missionKey; + uint64_t resourceAddr; + TileXRCcuCustomChannelCaps caps; +}; + +union TileXRCcuDataTypeUnion { + TileXRCcuDataByte8 byte8; + TileXRCcuDataByte32 byte32; + TileXRCcuDataByte64 byte64; + TileXRCcuBaseInfoData baseinfo; + TileXRCcuInstrInfo insinfo; + TileXRCcuDieInfo dieinfo; +}; + +struct TileXRCcuData { + uint32_t udieIdx; + uint32_t dataLen; + uint32_t dataArraySize; + TileXRCcuDataTypeUnion dataArray[8]; +}; + +union TileXRCcuDataUnion { + char raw[TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE]; + TileXRCcuData dataInfo; +}; + +struct TileXRCcuCustomChannelIn { + TileXRCcuDataUnion data; + uint32_t offsetStartIdx; + uint32_t op; +}; + +struct TileXRCcuCustomChannelOut { + TileXRCcuDataUnion data; + uint32_t offsetNextIdx; + int opRet; +}; + +struct TileXRCcuRaInfo { + int mode; + uint32_t phyId; +}; + +union TileXRCcuHccpEid { + uint8_t raw[TILEXR_CCU_EID_BYTES]; + struct { + uint64_t reserved; + uint32_t prefix; + uint32_t addr; + } in4; + struct { + uint64_t subnetPrefix; + uint64_t interfaceId; + } in6; +}; + +struct TileXRCcuHccpDevEidInfo { + char name[TILEXR_CCU_HCCP_DEV_EID_INFO_NAME_BYTES]; + uint32_t type; + uint32_t eidIndex; + TileXRCcuHccpEid eid; + uint32_t dieId; + uint32_t chipId; + uint32_t funcId; + uint32_t resv; +}; + +struct TileXRCcuHccpCtxInitCfg { + int mode; + union { + struct { + bool disabledLiteThread; + } rdma; + }; +}; + +struct TileXRCcuHccpCtxInitAttr { + uint32_t phyId; + union { + uint8_t rdmaPad[24]; + struct { + uint32_t eidIndex; + TileXRCcuHccpEid eid; + } ub; + }; + uint32_t resv[16]; +}; + +struct TileXRCcuHccpTokenId { + uint32_t tokenId; +}; + +struct TileXRCcuHccpMemKey { + uint8_t value[TILEXR_CCU_HCCP_MEM_KEY_BYTES]; + uint8_t size; +}; + +struct TileXRCcuHccpMemInfo { + uint64_t addr; + uint64_t size; +}; + +union TileXRCcuHccpRegSegFlag { + struct { + uint32_t tokenPolicy : 3; + uint32_t cacheable : 1; + uint32_t dsva : 1; + uint32_t access : 6; + uint32_t nonPin : 1; + uint32_t userIova : 1; + uint32_t tokenIdValid : 1; + uint32_t reserved : 18; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpMemRegAttr { + TileXRCcuHccpMemInfo mem; + union { + struct { + int access; + } rdma; + struct { + TileXRCcuHccpRegSegFlag flags; + uint32_t tokenValue; + void* tokenIdHandle; + } ub; + }; + uint32_t resv[8]; +}; + +struct TileXRCcuHccpMemRegInfo { + TileXRCcuHccpMemKey key; + union { + struct { + uint32_t lkey; + } rdma; + struct { + uint32_t tokenId; + uint64_t targetSegHandle; + } ub; + }; + uint32_t resv[8]; +}; + +struct TileXRCcuHccpMrRegInfo { + TileXRCcuHccpMemRegAttr in; + TileXRCcuHccpMemRegInfo out; +}; + +union TileXRCcuHccpDataPlaneCstmFlag { + struct { + uint32_t pollCqCstm : 1; + uint32_t reserved : 31; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpChanInfo { + struct { + TileXRCcuHccpDataPlaneCstmFlag dataPlaneFlag; + } in; + struct { + int fd; + } out; +}; + +union TileXRCcuHccpJfcFlag { + struct { + uint32_t lockFree : 1; + uint32_t jfcInline : 1; + uint32_t reserved : 30; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpCqInfo { + struct { + void* chanHandle; + uint32_t depth; + union { + struct { + uint64_t cqContext; + uint32_t mode; + uint32_t compVector; + } rdma; + struct { + uint64_t userCtx; + int mode; + uint32_t ceqn; + TileXRCcuHccpJfcFlag flag; + struct { + bool valid; + uint32_t cqeFlag; + } ccuExCfg; + } ub; + }; + } in; + struct { + uint64_t va; + uint32_t id; + uint32_t cqeSize; + uint64_t bufAddr; + uint64_t swdbAddr; + } out; +}; + +union TileXRCcuHccpJettyFlag { + struct { + uint32_t shareJfr : 1; + uint32_t reserved : 31; + } bs; + uint32_t value; +}; + +union TileXRCcuHccpJfsFlag { + struct { + uint32_t lockFree : 1; + uint32_t errorSuspend : 1; + uint32_t outorderComp : 1; + uint32_t orderType : 8; + uint32_t multiPath : 1; + uint32_t reserved : 20; + } bs; + uint32_t value; +}; + +union TileXRCcuHccpCstmJfsFlag { + struct { + uint32_t sqCstm : 1; + uint32_t dbCstm : 1; + uint32_t dbCtlCstm : 1; + uint32_t reserved : 29; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpJettyQueCfgEx { + uint32_t buffSize; + uint64_t buffVa; +}; + +struct TileXRCcuHccpQpCreateAttr { + void* scqHandle; + void* rcqHandle; + void* srqHandle; + uint32_t sqDepth; + uint32_t rqDepth; + int transportMode; + union { + struct { + uint32_t mode; + uint32_t udpSport; + uint8_t trafficClass; + uint8_t sl; + uint8_t timeout; + uint8_t rnrRetry; + uint8_t retryCnt; + } rdma; + struct { + int mode; + uint32_t jettyId; + TileXRCcuHccpJettyFlag flag; + TileXRCcuHccpJfsFlag jfsFlag; + void* tokenIdHandle; + uint32_t tokenValue; + uint8_t priority; + uint8_t rnrRetry; + uint8_t errTimeout; + union { + struct { + TileXRCcuHccpJettyQueCfgEx sq; + bool piType; + TileXRCcuHccpCstmJfsFlag cstmFlag; + uint32_t sqebbNum; + } extMode; + struct { + bool lockFlag; + uint32_t sqeBufIdx; + } taCacheMode; + }; + } ub; + }; + uint32_t resv[16]; +}; + +struct TileXRCcuHccpQpKey { + uint8_t value[TILEXR_CCU_HCCP_QP_KEY_BYTES]; + uint8_t size; +}; + +struct TileXRCcuHccpQpCreateInfo { + TileXRCcuHccpQpKey key; + union { + struct { + uint32_t qpn; + } rdma; + struct { + uint32_t uasid; + uint32_t id; + uint64_t sqBuffVa; + uint64_t wqebbSize; + uint64_t dbAddr; + uint32_t dbTokenId; + uint64_t ciAddr; + } ub; + }; + uint64_t va; + uint32_t resv[16]; +}; + +union TileXRCcuHccpImportJettyFlag { + struct { + uint32_t tokenPolicy : 3; + uint32_t orderType : 8; + uint32_t shareTp : 1; + uint32_t reserved : 20; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpJettyImportExpCfg { + uint64_t tpHandle; + uint64_t peerTpHandle; + uint64_t tag; + uint32_t txPsn; + uint32_t rxPsn; + uint32_t rsv[16]; +}; + +struct TileXRCcuHccpQpImportInfo { + struct { + TileXRCcuHccpQpKey key; + union { + struct { + int mode; + uint32_t tokenValue; + int policy; + int type; + TileXRCcuHccpImportJettyFlag flag; + TileXRCcuHccpJettyImportExpCfg expImportCfg; + uint32_t tpType; + } ub; + }; + uint32_t resv[7]; + } in; + struct { + union { + struct { + uint64_t tjettyHandle; + uint32_t tpn; + } ub; + }; + uint32_t resv[8]; + } out; +}; + +union TileXRCcuHccpGetTpCfgFlag { + struct { + uint32_t ctp : 1; + uint32_t rtp : 1; + uint32_t utp : 1; + uint32_t uboe : 1; + uint32_t preDefined : 1; + uint32_t dynamicDefined : 1; + uint32_t reserved : 26; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpGetTpCfg { + TileXRCcuHccpGetTpCfgFlag flag; + int transMode; + TileXRCcuHccpEid localEid; + TileXRCcuHccpEid peerEid; +}; + +struct TileXRCcuHccpTpInfo { + uint64_t tpHandle; + uint32_t resv; +}; + +struct TileXRCcuRaInitConfig { + uint32_t phyId; + uint32_t nicPosition; + int hdcType; + bool enableHdcAsync; +}; + +struct TileXRCcuRtProcExtParam { + const char* paramInfo; + uint64_t paramLen; +}; + +struct TileXRCcuRtNetServiceOpenArgs { + TileXRCcuRtProcExtParam* extParamList; + uint64_t extParamCnt; +}; + +struct TileXRCcuTlvInitInfo { + int version; + uint32_t phyId; + uint32_t nicPosition; + uint32_t reserved[16]; +}; + +struct TileXRCcuTlvMsg { + uint32_t type; + uint32_t length; + char* data; +}; + +struct TileXRCcuEndpointRouteProviderResourceWindow { + uint64_t addr = 0; + uint64_t bytes = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; +}; + +struct TileXRCcuEndpointRouteProviderRoute { + uint8_t remoteEid[TILEXR_CCU_EID_BYTES] = {}; + uint32_t tpn = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + bool endpointRouteVerified = false; +}; + +using TileXRCcuRaCustomChannelFunc = int (*)( + TileXRCcuRaInfo info, + TileXRCcuCustomChannelIn* in, + TileXRCcuCustomChannelOut* out); + +using TileXRCcuRtGetDevicePhyIdByIndexFunc = int (*)(uint32_t logicDevId, uint32_t* phyId); +using TileXRCcuRtOpenNetServiceFunc = int (*)(const TileXRCcuRtNetServiceOpenArgs* args); +using TileXRCcuRtCloseNetServiceFunc = int (*)(); + +using TileXRCcuRaInitFunc = int (*)(TileXRCcuRaInitConfig* config); +using TileXRCcuRaDeinitFunc = int (*)(TileXRCcuRaInitConfig* config); +using TileXRCcuRaTlvInitFunc = int (*)(TileXRCcuTlvInitInfo* initInfo, uint32_t* bufferSize, void** tlvHandle); +using TileXRCcuRaTlvRequestFunc = int (*)( + void* tlvHandle, + uint32_t moduleType, + TileXRCcuTlvMsg* sendMsg, + TileXRCcuTlvMsg* recvMsg); +using TileXRCcuRaTlvDeinitFunc = int (*)(void* tlvHandle); +using TileXRCcuRaGetDevEidInfoNumFunc = int (*)(TileXRCcuRaInfo info, uint32_t* num); +using TileXRCcuRaGetDevEidInfoListFunc = int (*)( + TileXRCcuRaInfo info, + TileXRCcuHccpDevEidInfo list[], + uint32_t* num); +using TileXRCcuRaCtxInitFunc = int (*)( + TileXRCcuHccpCtxInitCfg* cfg, + TileXRCcuHccpCtxInitAttr* attr, + void** ctx); +using TileXRCcuRaCtxDeinitFunc = int (*)(void* ctx); +using TileXRCcuRaCtxTokenIdAllocFunc = int (*)( + void* ctx, + TileXRCcuHccpTokenId* token, + void** tokenHandle); +using TileXRCcuRaCtxTokenIdFreeFunc = int (*)(void* ctx, void* tokenHandle); +using TileXRCcuRaCtxLmemRegisterFunc = int (*)( + void* ctx, + TileXRCcuHccpMrRegInfo* mr, + void** lmemHandle); +using TileXRCcuRaCtxLmemUnregisterFunc = int (*)(void* ctx, void* lmemHandle); +using TileXRCcuRaGetSecRandomFunc = int (*)(TileXRCcuRaInfo* info, uint32_t* value); +using TileXRCcuRaCtxChanCreateFunc = int (*)(void* ctx, TileXRCcuHccpChanInfo* info, void** chanHandle); +using TileXRCcuRaCtxChanDestroyFunc = int (*)(void* ctx, void* chanHandle); +using TileXRCcuRaCtxCqCreateFunc = int (*)(void* ctx, TileXRCcuHccpCqInfo* info, void** cqHandle); +using TileXRCcuRaCtxCqDestroyFunc = int (*)(void* ctx, void* cqHandle); +using TileXRCcuRaCtxQpCreateFunc = int (*)( + void* ctx, + TileXRCcuHccpQpCreateAttr* attr, + TileXRCcuHccpQpCreateInfo* info, + void** qpHandle); +using TileXRCcuRaCtxQpDestroyFunc = int (*)(void* qpHandle); +using TileXRCcuRaCtxQpImportFunc = int (*)( + void* ctx, + TileXRCcuHccpQpImportInfo* info, + void** remoteQpHandle); +using TileXRCcuRaCtxQpUnimportFunc = int (*)(void* ctx, void* remoteQpHandle); +using TileXRCcuRaCtxQpBindFunc = int (*)(void* qpHandle, void* remoteQpHandle); +using TileXRCcuRaCtxQpUnbindFunc = int (*)(void* qpHandle); +using TileXRCcuRaGetTpInfoListAsyncFunc = int (*)( + void* ctx, + TileXRCcuHccpGetTpCfg* cfg, + TileXRCcuHccpTpInfo infoList[], + uint32_t* num, + void** reqHandle); +using TileXRCcuRaGetAsyncReqResultFunc = int (*)(void* reqHandle, int* reqResult); + +using TileXRCcuEndpointRouteProviderFunc = int (*)( + uint32_t devicePhyId, + const TileXRCcuEndpointRouteProviderResourceWindow* localResourceWindow, + TileXRCcuEndpointRouteProviderRoute* route); + +static_assert(sizeof(TileXRCcuCustomChannelIn::data.raw) == TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE, + "CCU custom channel input must match HCCP custom_chan_info_in data size"); +static_assert(sizeof(TileXRCcuCustomChannelOut::data.raw) == TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE, + "CCU custom channel output must match HCCP custom_chan_info_out data size"); + +} // namespace TileXR + +#endif // TILEXR_CCU_HCCP_TYPES_H diff --git a/src/comm/ccu/tilexr_ccu_install_provider.cpp b/src/comm/ccu/tilexr_ccu_install_provider.cpp new file mode 100644 index 0000000..f0adbe7 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_install_provider.cpp @@ -0,0 +1,1534 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_install_provider.h" + +#include + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuInstallProviderReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRCcuInstallProviderReport{}; +} + +void ResetManifestReport(TileXRCcuInstallManifestReport* report) +{ + if (report == nullptr) { + return; + } + report->requirementCount = 0; + report->message.clear(); +} + +int FailManifest(TileXRCcuInstallManifest* manifest, TileXRCcuInstallManifestReport* report, const std::string& message) +{ + if (manifest != nullptr) { + *manifest = TileXRCcuInstallManifest{}; + } + if (report != nullptr) { + report->requirementCount = 0; + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +void MarkMissingStep(TileXRCcuInstallStepEvidence* step, const std::string& message) +{ + if (step == nullptr) { + return; + } + step->satisfied = false; + step->source.kind = TileXRCcuEvidenceKind::Missing; + step->source.surface = TileXRCcuEvidenceSurface::Unspecified; + step->source.source.clear(); + step->source.detail = message; + step->message = message; +} + +void FillUnsupportedReport(TileXRCcuInstallProviderReport* report) +{ + if (report == nullptr) { + return; + } + const std::string message = "no public no-hcomm CCU install provider is available"; + report->offlineOnly = true; + report->installAttempted = false; + report->installSucceeded = false; + report->message = message; + MarkMissingStep(&report->mission, message); + MarkMissingStep(&report->repository, message); + MarkMissingStep(&report->localXn, message); + MarkMissingStep(&report->remoteXn, message); + MarkMissingStep(&report->notifyCke, message); + MarkMissingStep(&report->channelBinding, message); +} + +void FillScopeFailureReport(TileXRCcuInstallProviderReport* report, const std::string& message) +{ + if (report == nullptr) { + return; + } + report->offlineOnly = true; + report->installAttempted = false; + report->installSucceeded = false; + report->message = message; + MarkMissingStep(&report->mission, message); + MarkMissingStep(&report->repository, message); + MarkMissingStep(&report->localXn, message); + MarkMissingStep(&report->remoteXn, message); + MarkMissingStep(&report->notifyCke, message); + MarkMissingStep(&report->channelBinding, message); +} + +TileXRCcuEvidenceSource MissingSource(const std::string& detail) +{ + TileXRCcuEvidenceSource source; + source.kind = TileXRCcuEvidenceKind::Missing; + source.surface = TileXRCcuEvidenceSurface::Unspecified; + source.detail = detail; + return source; +} + +TileXRCcuEvidenceSource SourceOrMissing(const TileXRCcuInstallStepEvidence& step, const std::string& detail) +{ + return step.satisfied ? step.source : MissingSource(detail); +} + +uint64_t MixReceiptWord(uint64_t hash, uint64_t value) +{ + constexpr uint64_t prime = 1099511628211ULL; + for (uint32_t i = 0; i < 8U; ++i) { + hash ^= static_cast((value >> (i * 8U)) & 0xffU); + hash *= prime; + } + return hash; +} + +uint64_t BuildRepositoryInstallReceiptId( + const TileXRCcuLaunchPackage& package, + const TileXRCcuRepositoryInstallReceipt& receipt) +{ + uint64_t hash = 1469598103934665603ULL; + hash = MixReceiptWord(hash, TileXRCcuComputeLaunchPackageFingerprint(package)); + hash = MixReceiptWord(hash, receipt.dieId); + hash = MixReceiptWord(hash, receipt.instructionStartId); + hash = MixReceiptWord(hash, receipt.instructionCount); + hash = MixReceiptWord(hash, receipt.instructionBytes); + hash = MixReceiptWord(hash, receipt.deviceInstructionAddr); + return hash == 0 ? 1 : hash; +} + +TileXRCcuInstallStepEvidence PublicVerifiedStep( + const TileXRCcuLaunchPackage& package, + const TileXRCcuInstallRequest& request, + uint64_t receiptId, + const std::string& source, + const std::string& detail, + bool endpointRouteVerified = false) +{ + TileXRCcuInstallStepEvidence step; + step.satisfied = true; + step.source.kind = TileXRCcuEvidenceKind::PublicVerified; + step.source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + step.source.packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + step.source.deviceId = request.deviceId; + step.source.rank = request.rank; + step.source.provider = request.provider; + step.source.installAttemptReceiptId = receiptId; + step.source.endpointRouteVerified = endpointRouteVerified; + step.source.source = source; + step.source.detail = detail; + step.message = detail; + return step; +} + +TileXRCcuInstallRequirement BaseRequirement( + TileXRCcuInstallRequirementKind kind, + uint64_t packageFingerprint, + uint8_t dieId, + const std::string& label, + const std::string& detail) +{ + TileXRCcuInstallRequirement requirement; + requirement.kind = kind; + requirement.packageFingerprint = packageFingerprint; + requirement.dieId = dieId; + requirement.label = label; + requirement.detail = detail; + return requirement; +} + +uint32_t TotalBindingCount(const std::vector& resources) +{ + uint32_t total = 0; + for (const auto& resource : resources) { + total += resource.bindingCount; + } + return total; +} + +bool SameBindingChannel( + const TileXRCcuRemoteXnBindingProof& lhs, + const TileXRCcuRemoteXnBindingProof& rhs) +{ + return lhs.dieId == rhs.dieId && lhs.channelId == rhs.channelId; +} + +bool HasInstalledChannel( + const std::vector& channels, + uint8_t dieId, + uint16_t channelId) +{ + for (const auto& channel : channels) { + if (channel.dieId == dieId && channel.channelId == channelId) { + return true; + } + } + return false; +} + +uint32_t CountUniqueRemoteBindingChannels(const std::vector& bindings) +{ + uint32_t count = 0; + for (size_t i = 0; i < bindings.size(); ++i) { + bool seen = false; + for (size_t j = 0; j < i; ++j) { + if (SameBindingChannel(bindings[i], bindings[j])) { + seen = true; + break; + } + } + if (!seen) { + ++count; + } + } + return count; +} + +uint32_t CountInstalledRemoteBindingChannels( + const std::vector& bindings, + const std::vector& channels) +{ + uint32_t count = 0; + for (size_t i = 0; i < bindings.size(); ++i) { + bool seen = false; + for (size_t j = 0; j < i; ++j) { + if (SameBindingChannel(bindings[i], bindings[j])) { + seen = true; + break; + } + } + if (!seen && HasInstalledChannel(channels, bindings[i].dieId, bindings[i].channelId)) { + ++count; + } + } + return count; +} + +const TileXRCcuInstallRequirement* FindRequirement( + const TileXRCcuInstallManifest& manifest, + TileXRCcuInstallRequirementKind kind); + +bool RangeContainsId(const TileXRCcuRange& range, uint8_t dieId, uint16_t id) +{ + if (range.dieId != dieId || range.num == 0) { + return false; + } + const uint32_t begin = range.startId; + const uint32_t end = begin + range.num; + return id >= begin && id < end; +} + +bool RepositoryMissionWindowContainsTask(const TileXRCcuRepositoryImage& repository, const TileXRCcuTask& task) +{ + if (repository.dieId != task.dieId || repository.missionCount == 0 || task.instCnt == 0) { + return false; + } + const uint32_t begin = repository.missionStartId; + const uint32_t end = begin + repository.missionCount; + const uint32_t taskBegin = task.instStartId; + const uint32_t taskEnd = taskBegin + task.instCnt; + return taskBegin >= begin && taskEnd <= end; +} + +bool FailMissionLaunchDescriptorProof(std::string* diagnostic, const std::string& reason) +{ + if (diagnostic != nullptr) { + *diagnostic = "mission/key launch task descriptor proof failed: " + reason; + } + return false; +} + +bool ValidateMissionLaunchDescriptorProof( + const TileXRCcuLaunchPackage& package, + const TileXRCcuInstallManifest* manifest, + std::string* diagnostic) +{ + if (diagnostic != nullptr) { + diagnostic->clear(); + } + if (!package.plan.mission.installed || package.plan.mission.key == 0) { + return FailMissionLaunchDescriptorProof(diagnostic, "mission key is not installed in the producer plan"); + } + if (!RangeContainsId( + package.plan.kernelLocalMission, + package.plan.mission.dieId, + package.plan.mission.missionId)) { + return FailMissionLaunchDescriptorProof( + diagnostic, + "mission id is outside the kernel-local mission range"); + } + if (manifest == nullptr) { + return FailMissionLaunchDescriptorProof(diagnostic, "install manifest mission requirement is missing"); + } + const TileXRCcuInstallRequirement* mission = + FindRequirement(*manifest, TileXRCcuInstallRequirementKind::MissionKey); + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (mission == nullptr || + mission->packageFingerprint != packageFingerprint || + mission->dieId != package.plan.mission.dieId || + mission->missionId != package.plan.mission.missionId || + mission->missionKey != package.plan.mission.key || + mission->resourceStartId != package.plan.kernelLocalMission.startId || + mission->resourceCount != package.plan.kernelLocalMission.num) { + return FailMissionLaunchDescriptorProof( + diagnostic, + "install manifest mission requirement does not match the launch package"); + } + if (package.repository.instructions.empty() || + package.repository.dieId != package.plan.mission.dieId || + package.repository.missionCount == 0) { + return FailMissionLaunchDescriptorProof(diagnostic, "repository mission instruction window is missing"); + } + if (package.tasks.empty()) { + return FailMissionLaunchDescriptorProof(diagnostic, "launch task descriptor list is missing"); + } + for (const auto& task : package.tasks) { + if (task.dieId != package.plan.mission.dieId || + task.missionId != package.plan.mission.missionId || + task.key != package.plan.mission.key) { + return FailMissionLaunchDescriptorProof( + diagnostic, + "launch task descriptor mission id or mission key does not match the producer plan"); + } + if (!RepositoryMissionWindowContainsTask(package.repository, task)) { + return FailMissionLaunchDescriptorProof( + diagnostic, + "launch task descriptor instruction range is outside the repository mission window"); + } + } + if (diagnostic != nullptr) { + *diagnostic = + "mission/key carried by launch task descriptor mission id and mission key within repository window"; + } + return true; +} + +bool FailRemoteXnExchangeBindingProof(std::string* diagnostic, const std::string& reason) +{ + if (diagnostic != nullptr) { + *diagnostic = "remote XN peer exchange proof failed: " + reason; + } + return false; +} + +bool HasInstalledChannelRoute( + const TileXRCcuLowerLayerInstallPlan& lowerLayerPlan, + uint8_t dieId, + uint16_t channelId) +{ + for (const auto& channel : lowerLayerPlan.channels) { + if (channel.dieId == dieId && channel.channelId == channelId) { + return true; + } + } + return false; +} + +bool HasVerifiedEndpointRoutes(const TileXRCcuLowerLayerInstallPlan* lowerLayerPlan) +{ + if (lowerLayerPlan == nullptr || lowerLayerPlan->remoteXnBindings.empty()) { + return false; + } + for (const auto& proof : lowerLayerPlan->remoteXnBindings) { + if (!proof.peerExchangeObserved || !proof.endpointRouteVerified || + !HasInstalledChannelRoute(*lowerLayerPlan, proof.dieId, proof.channelId)) { + return false; + } + } + return true; +} + +bool HasVerifiedChannelResourceBindings(const TileXRCcuLowerLayerInstallPlan* lowerLayerPlan) +{ + if (lowerLayerPlan == nullptr || lowerLayerPlan->remoteXnBindings.empty()) { + return false; + } + for (const auto& proof : lowerLayerPlan->remoteXnBindings) { + if (!proof.peerExchangeObserved || + !proof.endpointRouteVerified || + !proof.channelResourceOwnerVerified || + !proof.transportResourceExchangeVerified || + !HasInstalledChannelRoute(*lowerLayerPlan, proof.dieId, proof.channelId)) { + return false; + } + } + return true; +} + +bool RequiresRemoteXnBindingEvidence(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXn || + mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +bool RequiresChannelBindingEvidence(TileXRCcuBarrierMode mode) +{ + return mode != TileXRCcuBarrierMode::LocalCke && + mode != TileXRCcuBarrierMode::LocalCkePostOnly; +} + +uint16_t EffectiveResourceLocalWaitCke(const TileXRCcuSyncResource& resource) +{ + return resource.localWaitCke == 0 ? resource.notifyCke : resource.localWaitCke; +} + +uint16_t EffectiveProofLocalWaitCke(const TileXRCcuRemoteXnBindingProof& proof) +{ + return proof.localWaitCke == 0 ? proof.notifyCke : proof.localWaitCke; +} + +bool ValidateRemoteXnExchangeBindingProof( + const TileXRCcuLaunchPackage& package, + const TileXRCcuInstallManifest* manifest, + const TileXRCcuLowerLayerInstallPlan* lowerLayerPlan, + std::string* diagnostic) +{ + if (diagnostic != nullptr) { + diagnostic->clear(); + } + if (manifest == nullptr) { + return FailRemoteXnExchangeBindingProof(diagnostic, "install manifest remote XN requirement is missing"); + } + const TileXRCcuInstallRequirement* remoteXn = + FindRequirement(*manifest, TileXRCcuInstallRequirementKind::RemoteXnBinding); + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (remoteXn == nullptr || + remoteXn->packageFingerprint != packageFingerprint || + remoteXn->syncResourceCount != package.plan.syncResources.size()) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "install manifest remote XN requirement does not match the launch package"); + } + if (lowerLayerPlan == nullptr || lowerLayerPlan->remoteXnBindings.empty()) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "remote XN install provider is missing; peer exchange proof is missing"); + } + if (lowerLayerPlan->remoteXnBindings.size() != package.plan.syncResources.size()) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "peer exchange proof count does not match sync resource count"); + } + + for (const auto& resource : package.plan.syncResources) { + bool identityMatched = false; + bool remoteNotifyCkeMismatch = false; + bool localWaitCkeMismatch = false; + bool matched = false; + for (const auto& proof : lowerLayerPlan->remoteXnBindings) { + if (proof.dieId != resource.dieId || + proof.channelId != resource.channelId || + proof.localXn != resource.localXn || + proof.remoteXn != resource.remoteXn) { + continue; + } + identityMatched = true; + if (proof.notifyCke != resource.notifyCke) { + remoteNotifyCkeMismatch = true; + continue; + } + if (EffectiveProofLocalWaitCke(proof) != EffectiveResourceLocalWaitCke(resource)) { + localWaitCkeMismatch = true; + continue; + } + if (!proof.peerExchangeObserved) { + return FailRemoteXnExchangeBindingProof(diagnostic, "peer exchange was not observed"); + } + if (!HasInstalledChannelRoute(*lowerLayerPlan, proof.dieId, proof.channelId)) { + return FailRemoteXnExchangeBindingProof(diagnostic, "matching channel route is not installed"); + } + if (!proof.endpointRouteVerified) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "endpoint route provenance was not verified"); + } + if (!proof.channelResourceOwnerVerified) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "channel resource owner did not verify channel-bound remote XN allocation"); + } + if (!proof.transportResourceExchangeVerified) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "transport resource exchange did not verify remote XN and notify CKE binding"); + } + matched = true; + break; + } + if (!matched) { + if (identityMatched && remoteNotifyCkeMismatch) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "syncXn remote notify CKE is not covered by peer exchange proof"); + } + if (identityMatched && localWaitCkeMismatch) { + return FailRemoteXnExchangeBindingProof( + diagnostic, + "syncXn local wait CKE is not covered by peer exchange proof"); + } + return FailRemoteXnExchangeBindingProof( + diagnostic, + "syncXn remote XN operand is not covered by peer exchange proof"); + } + } + + if (diagnostic != nullptr) { + *diagnostic = + "remote XN peer exchange proof matches syncXn operands, verified endpoint route channel contexts, " + "channel resource owner allocation, and transport resource exchange"; + } + return true; +} + +int ValidateInstallManifestScope( + const TileXRCcuLaunchPackage& package, + uint64_t packageFingerprint, + TileXRCcuInstallManifest* manifest, + TileXRCcuInstallManifestReport* report) +{ + if (package.tasks.empty()) { + return FailManifest(manifest, report, "missing CCU launch tasks for install manifest"); + } + if (package.repository.instructions.empty()) { + return FailManifest(manifest, report, "missing CCU repository image for install manifest"); + } + if (package.installScope.packageFingerprint == 0 || package.installScope.provider.empty()) { + return FailManifest(manifest, report, "launch install scope is not bound"); + } + if (package.installScope.packageFingerprint != packageFingerprint) { + return FailManifest(manifest, report, "launch install scope is stale"); + } + return TILEXR_SUCCESS; +} + +bool HasRequirementKind(const TileXRCcuInstallManifest& manifest, TileXRCcuInstallRequirementKind kind) +{ + bool found = false; + for (const auto& requirement : manifest.requirements) { + if (requirement.kind != kind) { + continue; + } + if (found) { + return false; + } + found = true; + } + return found; +} + +const TileXRCcuInstallRequirement* FindRequirement( + const TileXRCcuInstallManifest& manifest, + TileXRCcuInstallRequirementKind kind) +{ + for (const auto& requirement : manifest.requirements) { + if (requirement.kind == kind) { + return &requirement; + } + } + return nullptr; +} + +int ValidateRequirementMetadata( + const TileXRCcuInstallRequirement* requirement, + uint64_t packageFingerprint, + TileXRCcuInstallProviderReport* report) +{ + if (requirement == nullptr || requirement->label.empty() || requirement->detail.empty() || + requirement->packageFingerprint != packageFingerprint) { + FillScopeFailureReport(report, "install manifest requirement metadata mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +bool IsPublicVerifiedStep(const TileXRCcuInstallStepEvidence& step) +{ + return step.satisfied && step.source.kind == TileXRCcuEvidenceKind::PublicVerified; +} + +uint32_t CountPublicVerifiedSteps(const TileXRCcuInstallProviderReport& report) +{ + uint32_t count = 0; + count += IsPublicVerifiedStep(report.mission) ? 1U : 0U; + count += IsPublicVerifiedStep(report.repository) ? 1U : 0U; + count += IsPublicVerifiedStep(report.localXn) ? 1U : 0U; + count += IsPublicVerifiedStep(report.remoteXn) ? 1U : 0U; + count += IsPublicVerifiedStep(report.notifyCke) ? 1U : 0U; + count += IsPublicVerifiedStep(report.channelBinding) ? 1U : 0U; + return count; +} + +bool IsRequirementSatisfied( + TileXRCcuInstallRequirementKind kind, + const TileXRCcuInstallProviderReport& report) +{ + switch (kind) { + case TileXRCcuInstallRequirementKind::MissionKey: + return IsPublicVerifiedStep(report.mission); + case TileXRCcuInstallRequirementKind::RepositoryImage: + return IsPublicVerifiedStep(report.repository); + case TileXRCcuInstallRequirementKind::LocalXn: + return IsPublicVerifiedStep(report.localXn); + case TileXRCcuInstallRequirementKind::RemoteXnBinding: + return IsPublicVerifiedStep(report.remoteXn); + case TileXRCcuInstallRequirementKind::NotifyCke: + return IsPublicVerifiedStep(report.notifyCke); + case TileXRCcuInstallRequirementKind::ChannelBinding: + return IsPublicVerifiedStep(report.channelBinding); + default: + return false; + } +} + +uint32_t CountPublicVerifiedRequiredSteps( + const TileXRCcuInstallManifest& manifest, + const TileXRCcuInstallProviderReport& report) +{ + uint32_t count = 0; + for (const auto& requirement : manifest.requirements) { + count += IsRequirementSatisfied(requirement.kind, report) ? 1U : 0U; + } + return count; +} + +void FillManifestInstallSurfaceCounts( + const TileXRCcuInstallManifest* manifest, + TileXRCcuInstallProviderReport* report) +{ + if (manifest == nullptr || report == nullptr) { + return; + } + report->requiredInstallSurfaceCount = static_cast(manifest->requirements.size()); + report->publicVerifiedInstallSurfaceCount = CountPublicVerifiedRequiredSteps(*manifest, *report); + report->missingInstallSurfaceCount = + report->requiredInstallSurfaceCount > report->publicVerifiedInstallSurfaceCount ? + report->requiredInstallSurfaceCount - report->publicVerifiedInstallSurfaceCount : + 0U; +} + +bool RejectOfflinePublicVerified(const TileXRCcuInstallProviderReport& installReport) +{ + return installReport.offlineOnly && + (IsPublicVerifiedStep(installReport.mission) || + IsPublicVerifiedStep(installReport.repository) || + IsPublicVerifiedStep(installReport.localXn) || + IsPublicVerifiedStep(installReport.remoteXn) || + IsPublicVerifiedStep(installReport.notifyCke) || + IsPublicVerifiedStep(installReport.channelBinding)); +} + +bool HasPublicVerifiedStep(const TileXRCcuInstallProviderReport& installReport) +{ + return IsPublicVerifiedStep(installReport.mission) || + IsPublicVerifiedStep(installReport.repository) || + IsPublicVerifiedStep(installReport.localXn) || + IsPublicVerifiedStep(installReport.remoteXn) || + IsPublicVerifiedStep(installReport.notifyCke) || + IsPublicVerifiedStep(installReport.channelBinding); +} + +int ValidateInstallReceiptStep( + const std::string& label, + uint64_t expectedReceiptId, + const TileXRCcuInstallStepEvidence& step) +{ + (void)label; + if (!IsPublicVerifiedStep(step)) { + return TILEXR_SUCCESS; + } + if (expectedReceiptId == 0 || step.source.installAttemptReceiptId == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (step.source.installAttemptReceiptId != expectedReceiptId) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int ValidatePublicVerifiedStepScope(const std::string& label, const TileXRCcuInstallStepEvidence& step) +{ + (void)label; + if (!IsPublicVerifiedStep(step)) { + return TILEXR_SUCCESS; + } + if (step.source.surface != TileXRCcuEvidenceSurface::PublicInstallProvider || + step.source.packageFingerprint == 0 || + step.source.provider.empty() || + step.source.source.empty() || + step.source.detail.empty()) { + const std::string reason = "public verified evidence scope is incomplete"; + (void)reason; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int ValidateInstallReceipt(const TileXRCcuInstallProviderReport& installReport) +{ + if (HasPublicVerifiedStep(installReport) && + (!installReport.installAttempted || !installReport.installSucceeded)) { + const std::string reason = "install attempt did not succeed"; + (void)reason; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("mission/key", installReport.mission) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("repository", installReport.repository) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("local XN", installReport.localXn) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("remote XN", installReport.remoteXn) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("notify CKE", installReport.notifyCke) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidatePublicVerifiedStepScope("channel binding", installReport.channelBinding) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "mission/key", installReport.installAttemptReceiptId, installReport.mission) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "repository", installReport.installAttemptReceiptId, installReport.repository) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "local XN", installReport.installAttemptReceiptId, installReport.localXn) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "remote XN", installReport.installAttemptReceiptId, installReport.remoteXn) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "notify CKE", installReport.installAttemptReceiptId, installReport.notifyCke) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceiptStep( + "channel binding", installReport.installAttemptReceiptId, installReport.channelBinding) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int ValidateInstallRequestScope(const TileXRCcuInstallRequest& request, TileXRCcuInstallProviderReport* report) +{ + const TileXRCcuLaunchPackage& package = *request.package; + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (package.installScope.packageFingerprint == 0 || + package.installScope.packageFingerprint != packageFingerprint || + package.installScope.provider.empty()) { + FillScopeFailureReport(report, "launch install scope is not bound"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (request.deviceId != package.installScope.deviceId) { + FillScopeFailureReport(report, "device scope mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (request.rank != package.installScope.rank) { + FillScopeFailureReport(report, "rank scope mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (request.provider.empty() || request.provider != package.installScope.provider) { + FillScopeFailureReport(report, "provider scope mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int ValidateInstallRequestManifest(const TileXRCcuInstallRequest& request, TileXRCcuInstallProviderReport* report) +{ + if (request.manifest == nullptr) { + return TILEXR_SUCCESS; + } + + const TileXRCcuLaunchPackage& package = *request.package; + const TileXRCcuInstallManifest& manifest = *request.manifest; + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (manifest.packageFingerprint == 0 || manifest.packageFingerprint != packageFingerprint) { + FillScopeFailureReport(report, "install manifest fingerprint mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (manifest.deviceId != request.deviceId) { + FillScopeFailureReport(report, "install manifest device mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (manifest.rank != request.rank) { + FillScopeFailureReport(report, "install manifest rank mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (manifest.provider.empty() || manifest.provider != request.provider) { + FillScopeFailureReport(report, "install manifest provider mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (manifest.requiredEvidenceKind != TileXRCcuEvidenceKind::PublicVerified || + manifest.requiredEvidenceSurface != TileXRCcuEvidenceSurface::PublicInstallProvider) { + FillScopeFailureReport(report, "install manifest evidence contract mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!manifest.installAttemptReceiptRequired) { + FillScopeFailureReport(report, "install manifest receipt contract mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + uint32_t expectedRequirementCount = 4U; + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode)) { + ++expectedRequirementCount; + } + if (RequiresChannelBindingEvidence(package.plan.barrierMode)) { + ++expectedRequirementCount; + } + if (manifest.requirements.size() != expectedRequirementCount) { + FillScopeFailureReport(report, "install manifest requirement count mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::MissionKey) || + !HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::RepositoryImage) || + !HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::LocalXn) || + !HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::NotifyCke)) { + FillScopeFailureReport(report, "install manifest requirement kind mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode) != + HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::RemoteXnBinding)) { + FillScopeFailureReport(report, "install manifest remote XN requirement kind mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresChannelBindingEvidence(package.plan.barrierMode) != + HasRequirementKind(manifest, TileXRCcuInstallRequirementKind::ChannelBinding)) { + FillScopeFailureReport(report, "install manifest channel binding requirement kind mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + if (manifest.requiresHardwareInstall != package.requiresHardwareInstall) { + FillScopeFailureReport(report, "install manifest hardware requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const TileXRCcuInstallRequirement* mission = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::MissionKey); + if (ValidateRequirementMetadata(mission, packageFingerprint, report) != TILEXR_SUCCESS || + mission->dieId != package.plan.mission.dieId || + mission->missionId != package.plan.mission.missionId || + mission->missionKey != package.plan.mission.key || + mission->resourceStartId != package.plan.kernelLocalMission.startId || + mission->resourceCount != package.plan.kernelLocalMission.num) { + FillScopeFailureReport(report, "install manifest mission requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const TileXRCcuInstallRequirement* repository = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::RepositoryImage); + if (ValidateRequirementMetadata(repository, packageFingerprint, report) != TILEXR_SUCCESS || + repository->dieId != package.repository.dieId || + repository->repositoryStartId != package.repository.repositoryStartId || + repository->repositoryCount != package.repository.repositoryCount || + repository->missionStartId != package.repository.missionStartId || + repository->missionCount != package.repository.missionCount || + repository->instructionCount != package.repository.instructions.size()) { + FillScopeFailureReport(report, "install manifest repository requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const TileXRCcuInstallRequirement* localXn = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::LocalXn); + if (ValidateRequirementMetadata(localXn, packageFingerprint, report) != TILEXR_SUCCESS || + localXn->dieId != package.plan.kernelLocalXn.dieId || + localXn->resourceStartId != package.plan.kernelLocalXn.startId || + localXn->resourceCount != package.plan.kernelLocalXn.num) { + FillScopeFailureReport(report, "install manifest local XN requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode)) { + const TileXRCcuInstallRequirement* remoteXn = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::RemoteXnBinding); + if (ValidateRequirementMetadata(remoteXn, packageFingerprint, report) != TILEXR_SUCCESS || + remoteXn->dieId != package.plan.kernelLocalXn.dieId || + remoteXn->syncResourceCount != package.plan.syncResources.size()) { + FillScopeFailureReport(report, "install manifest remote XN requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + + const TileXRCcuInstallRequirement* notifyCke = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::NotifyCke); + if (ValidateRequirementMetadata(notifyCke, packageFingerprint, report) != TILEXR_SUCCESS || + notifyCke->dieId != package.plan.kernelLocalCke.dieId || + notifyCke->resourceStartId != package.plan.kernelLocalCke.startId || + notifyCke->resourceCount != package.plan.kernelLocalCke.num || + notifyCke->syncResourceCount != package.plan.syncResources.size()) { + FillScopeFailureReport(report, "install manifest notify CKE requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + if (RequiresChannelBindingEvidence(package.plan.barrierMode)) { + const TileXRCcuInstallRequirement* channel = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::ChannelBinding); + if (ValidateRequirementMetadata(channel, packageFingerprint, report) != TILEXR_SUCCESS || + channel->dieId != package.plan.kernelLocalXn.dieId || + channel->syncResourceCount != package.plan.syncResources.size() || + channel->bindingCount != TotalBindingCount(package.plan.syncResources)) { + FillScopeFailureReport(report, "install manifest channel requirement mismatch"); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + return TILEXR_SUCCESS; +} + +bool HasRepositoryInstallProviderInputs(const TileXRCcuInstallRequest& request) +{ + return !request.offlineOnly && + request.driverAdapter != nullptr && + request.repositoryMemoryOps.alloc != nullptr && + request.repositoryMemoryOps.copyHostToDevice != nullptr && + request.repositoryMemoryOps.free != nullptr && + request.repositoryReceipt != nullptr; +} + +struct TileXRCcuLowerLayerInstallResult { + uint32_t msidTokenCount = 0; + uint32_t pfeCount = 0; + uint32_t jettyCount = 0; + uint32_t channelCount = 0; + uint32_t xnClearCount = 0; + uint32_t ckeClearCount = 0; + bool localXnInstalled = false; + bool notifyCkeInstalled = false; + bool channelBindingInstalled = false; + std::string message; +}; + +std::string FormatLowerLayerPreconditionSummary(const TileXRCcuLowerLayerInstallResult& result) +{ + std::ostringstream summary; + summary << "lowerLayerPreconditions{" + << "msidTokenCount=" << result.msidTokenCount + << " pfeCount=" << result.pfeCount + << " jettyCount=" << result.jettyCount + << " channelCount=" << result.channelCount + << " xnClearCount=" << result.xnClearCount + << " ckeClearCount=" << result.ckeClearCount + << " localXnInstalled=" << (result.localXnInstalled ? 1 : 0) + << " notifyCkeInstalled=" << (result.notifyCkeInstalled ? 1 : 0) + << " channelBindingInstalled=" << (result.channelBindingInstalled ? 1 : 0) + << "}"; + return summary.str(); +} + +std::string FormatLowerLayerPlanSummary(const TileXRCcuLowerLayerInstallPlan& plan) +{ + std::ostringstream summary; + if (!plan.msidTokens.empty()) { + const auto& token = plan.msidTokens.front(); + summary << " msidToken0{dieId=" << static_cast(token.dieId) + << " msId=" << token.msId + << " tokenId=0x" << std::hex << token.tokenId + << " tokenValue=0x" << token.tokenValue << std::dec + << "}"; + } + if (!plan.pfes.empty()) { + const auto& pfe = plan.pfes.front(); + summary << " pfe0{dieId=" << static_cast(pfe.dieId) + << " offset=" << pfe.pfeOffset + << "}"; + } + if (!plan.jettys.empty()) { + const auto& jetty = plan.jettys.front(); + summary << " jetty0{dieId=" << static_cast(jetty.dieId) + << " startJettyCtxId=" << jetty.startJettyCtxId + << " ctxCount=" << jetty.ctxs.size() + << "}"; + } + if (!plan.channels.empty()) { + const auto& channel = plan.channels.front(); + summary << " channel0{dieId=" << static_cast(channel.dieId) + << " channelId=" << channel.channelId + << "}"; + } + if (!plan.xnClears.empty()) { + const auto& xn = plan.xnClears.front(); + summary << " xnClear0{dieId=" << static_cast(xn.dieId) + << " startXnId=" << xn.startXnId + << " count=" << xn.count + << "}"; + } + if (!plan.ckeClears.empty()) { + const auto& cke = plan.ckeClears.front(); + summary << " ckeClear0{dieId=" << static_cast(cke.dieId) + << " startCkeId=" << cke.startCkeId + << " count=" << cke.count + << "}"; + } + if (!plan.remoteXnBindings.empty()) { + const auto& remote = plan.remoteXnBindings.front(); + summary << " remoteXn0{dieId=" << static_cast(remote.dieId) + << " channelId=" << remote.channelId + << " localXn=" << remote.localXn + << " remoteXn=" << remote.remoteXn + << " notifyCke=" << remote.notifyCke + << " localWaitCke=" << remote.localWaitCke + << " peerExchangeObserved=" << (remote.peerExchangeObserved ? 1 : 0) + << " endpointRouteVerified=" << (remote.endpointRouteVerified ? 1 : 0) + << " channelResourceOwnerVerified=" << (remote.channelResourceOwnerVerified ? 1 : 0) + << " transportResourceExchangeVerified=" << (remote.transportResourceExchangeVerified ? 1 : 0) + << "}"; + } + return summary.str(); +} + +int InstallLowerLayerResources( + const TileXRCcuDriverAdapter& adapter, + const TileXRCcuLowerLayerInstallPlan& plan, + TileXRCcuLowerLayerInstallResult* result) +{ + if (result == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *result = TileXRCcuLowerLayerInstallResult{}; + + TileXRCcuDriverAdapterReport driverReport; + for (const auto& token : plan.msidTokens) { + const int ret = adapter.InstallMsidToken( + token.dieId, + token.msId, + token.tokenId, + token.tokenValue, + &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to install CCU MSID token via SET_MSID_TOKEN: " + driverReport.message; + return ret; + } + ++result->msidTokenCount; + } + + for (const auto& pfe : plan.pfes) { + const int ret = adapter.InstallPfeCtx(pfe.dieId, pfe.pfeOffset, pfe.ctx, &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to install CCU PFE context via SET_PFE: " + driverReport.message; + return ret; + } + ++result->pfeCount; + } + + for (const auto& jetty : plan.jettys) { + const TileXRCcuLocalJettyCtxData* ctxs = jetty.ctxs.empty() ? nullptr : jetty.ctxs.data(); + const int ret = adapter.InstallJettyCtx( + jetty.dieId, + jetty.startJettyCtxId, + ctxs, + static_cast(jetty.ctxs.size()), + &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to install CCU local jetty contexts via SET_JETTY_CTX: " + + driverReport.message; + return ret; + } + ++result->jettyCount; + } + + for (const auto& channel : plan.channels) { + const int ret = adapter.InstallChannelCtxV1(channel.dieId, channel.channelId, channel.ctx, &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to install CCU channel context via SET_CHANNEL: " + driverReport.message; + return ret; + } + ++result->channelCount; + } + + for (const auto& xn : plan.xnClears) { + const int ret = adapter.InstallXnRange(xn.dieId, xn.startXnId, xn.count, &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to install CCU local XN range via SET_XN: " + driverReport.message; + return ret; + } + ++result->xnClearCount; + } + + for (const auto& cke : plan.ckeClears) { + const int ret = adapter.ClearCkeRange(cke.dieId, cke.startCkeId, cke.count, &driverReport); + if (ret != TILEXR_SUCCESS) { + result->message = "failed to clear CCU CKE range via SET_CKE: " + driverReport.message; + return ret; + } + ++result->ckeClearCount; + } + + result->localXnInstalled = result->xnClearCount > 0; + result->notifyCkeInstalled = result->ckeClearCount > 0; + const uint32_t expectedChannelCount = plan.remoteXnBindings.empty() ? + static_cast(plan.channels.size()) : + CountUniqueRemoteBindingChannels(plan.remoteXnBindings); + const uint32_t installedChannelCount = plan.remoteXnBindings.empty() ? + static_cast(result->channelCount) : + CountInstalledRemoteBindingChannels(plan.remoteXnBindings, plan.channels); + result->channelBindingInstalled = + result->pfeCount > 0 && result->jettyCount > 0 && expectedChannelCount > 0 && + installedChannelCount >= expectedChannelCount; + result->message = + "lower-layer CCU resources installed via SET_MSID_TOKEN, SET_PFE, SET_JETTY_CTX, SET_CHANNEL, SET_XN, SET_CKE"; + return TILEXR_SUCCESS; +} + +int InstallRepositoryImageForRequest( + const TileXRCcuInstallRequest& request, + TileXRCcuRepositoryReport* repositoryReport) +{ + return TileXRCcuInstallRepositoryImageWithOptions( + request.package->repository, + request.repositoryInstallOptions, + request.repositoryMemoryOps, + request.repositoryMemoryUserData, + *request.driverAdapter, + request.repositoryReceipt, + repositoryReport); +} + +int InstallRepositoryOnly( + const TileXRCcuInstallRequest& request, + TileXRCcuHardwareInstallEvidence* evidence, + TileXRCcuInstallProviderReport* report) +{ + const TileXRCcuLaunchPackage& package = *request.package; + TileXRCcuRepositoryReport repositoryReport; + TileXRCcuLowerLayerInstallResult lowerLayerResult; + + int lowerLayerRet = TILEXR_SUCCESS; + if (request.installOrder == TileXRCcuInstallOrder::InstallLowerLayerFirst && + request.lowerLayerPlan != nullptr) { + lowerLayerRet = InstallLowerLayerResources(*request.driverAdapter, *request.lowerLayerPlan, &lowerLayerResult); + if (lowerLayerRet != TILEXR_SUCCESS) { + TileXRCcuInstallProviderReport nextReport; + nextReport.offlineOnly = false; + nextReport.installAttempted = true; + nextReport.installSucceeded = false; + nextReport.message = lowerLayerResult.message; + MarkMissingStep(&nextReport.mission, "mission/key install provider is not implemented"); + MarkMissingStep(&nextReport.repository, "repository install skipped after lower-layer install failure"); + MarkMissingStep(&nextReport.localXn, lowerLayerResult.message); + MarkMissingStep(&nextReport.remoteXn, "remote XN binding provider is not implemented"); + MarkMissingStep(&nextReport.notifyCke, lowerLayerResult.message); + MarkMissingStep(&nextReport.channelBinding, lowerLayerResult.message); + FillManifestInstallSurfaceCounts(request.manifest, &nextReport); + if (report != nullptr) { + *report = nextReport; + } + if (TileXRCcuBuildInstallEvidence(nextReport, evidence) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + return lowerLayerRet; + } + } + + const int installRet = InstallRepositoryImageForRequest(request, &repositoryReport); + + TileXRCcuInstallProviderReport nextReport; + nextReport.offlineOnly = false; + nextReport.installAttempted = true; + + if (installRet != TILEXR_SUCCESS) { + nextReport.installSucceeded = false; + const bool lowerLayerFirstAttempted = + request.installOrder == TileXRCcuInstallOrder::InstallLowerLayerFirst && + request.lowerLayerPlan != nullptr; + const std::string lowerLayerSummary = lowerLayerFirstAttempted ? + FormatLowerLayerPreconditionSummary(lowerLayerResult) + + FormatLowerLayerPlanSummary(*request.lowerLayerPlan) : + std::string(); + const std::string repositoryFailureMessage = lowerLayerFirstAttempted ? + lowerLayerSummary + "; " + repositoryReport.message : + repositoryReport.message; + nextReport.message = repositoryFailureMessage; + MarkMissingStep(&nextReport.mission, "mission/key install provider is not implemented"); + MarkMissingStep(&nextReport.repository, repositoryReport.message); + MarkMissingStep( + &nextReport.localXn, + lowerLayerFirstAttempted ? lowerLayerSummary : "local XN install provider is not implemented"); + MarkMissingStep( + &nextReport.remoteXn, + lowerLayerFirstAttempted ? lowerLayerSummary : "remote XN binding provider is not implemented"); + MarkMissingStep( + &nextReport.notifyCke, + lowerLayerFirstAttempted ? lowerLayerSummary : "notify CKE install provider is not implemented"); + MarkMissingStep( + &nextReport.channelBinding, + lowerLayerFirstAttempted ? lowerLayerSummary : "channel binding provider is not implemented"); + FillManifestInstallSurfaceCounts(request.manifest, &nextReport); + if (report != nullptr) { + *report = nextReport; + } + if (TileXRCcuBuildInstallEvidence(nextReport, evidence) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + return installRet; + } + + nextReport.installSucceeded = true; + nextReport.installAttemptReceiptId = BuildRepositoryInstallReceiptId(package, *request.repositoryReceipt); + + if (request.lowerLayerPlan != nullptr && + request.installOrder != TileXRCcuInstallOrder::InstallLowerLayerFirst) { + lowerLayerRet = InstallLowerLayerResources(*request.driverAdapter, *request.lowerLayerPlan, &lowerLayerResult); + if (lowerLayerRet != TILEXR_SUCCESS) { + nextReport.installSucceeded = false; + nextReport.message = lowerLayerResult.message; + MarkMissingStep(&nextReport.mission, "mission/key install provider is not implemented"); + MarkMissingStep( + &nextReport.repository, + "repository install evidence withheld after lower-layer install failure"); + MarkMissingStep(&nextReport.localXn, lowerLayerResult.message); + MarkMissingStep(&nextReport.remoteXn, "remote XN binding provider is not implemented"); + MarkMissingStep(&nextReport.notifyCke, lowerLayerResult.message); + MarkMissingStep(&nextReport.channelBinding, lowerLayerResult.message); + FillManifestInstallSurfaceCounts(request.manifest, &nextReport); + if (report != nullptr) { + *report = nextReport; + } + if (TileXRCcuBuildInstallEvidence(nextReport, evidence) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + return lowerLayerRet; + } + } + + std::string missionProofMessage; + if (ValidateMissionLaunchDescriptorProof(package, request.manifest, &missionProofMessage)) { + nextReport.mission = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "ValidateMissionLaunchDescriptorProof", + missionProofMessage); + } else { + MarkMissingStep(&nextReport.mission, missionProofMessage); + } + nextReport.repository = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "TileXRCcuInstallRepositoryImage", + "repository instruction image installed via SET_INSTRUCTION"); + if (lowerLayerResult.localXnInstalled) { + nextReport.localXn = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "InstallLowerLayerResources", + "kernel-local XN resources initialized via SET_XN"); + } else { + MarkMissingStep(&nextReport.localXn, "local XN install provider is not implemented"); + } + std::string remoteXnProofMessage; + if (!RequiresRemoteXnBindingEvidence(package.plan.barrierMode)) { + MarkMissingStep( + &nextReport.remoteXn, + "remote XN binding is not required for this CCU barrier mode"); + } else if (ValidateRemoteXnExchangeBindingProof( + package, + request.manifest, + request.lowerLayerPlan, + &remoteXnProofMessage)) { + nextReport.remoteXn = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "ValidateRemoteXnExchangeBindingProof", + remoteXnProofMessage, + true); + } else { + MarkMissingStep(&nextReport.remoteXn, remoteXnProofMessage); + } + if (lowerLayerResult.notifyCkeInstalled) { + nextReport.notifyCke = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "InstallLowerLayerResources", + "notify CKE resources cleared via SET_CKE"); + } else { + MarkMissingStep(&nextReport.notifyCke, "notify CKE install provider is not implemented"); + } + if (!RequiresChannelBindingEvidence(package.plan.barrierMode)) { + MarkMissingStep( + &nextReport.channelBinding, + "channel binding is not required for this CCU barrier mode"); + } else if (lowerLayerResult.channelBindingInstalled && HasVerifiedChannelResourceBindings(request.lowerLayerPlan)) { + nextReport.channelBinding = PublicVerifiedStep( + package, + request, + nextReport.installAttemptReceiptId, + "InstallLowerLayerResources", + "channel binding contexts installed via SET_PFE, SET_JETTY_CTX, SET_CHANNEL with verified endpoint routes, " + "channel resource owner allocation, and transport resource exchange", + true); + } else if (lowerLayerResult.channelBindingInstalled && HasVerifiedEndpointRoutes(request.lowerLayerPlan)) { + MarkMissingStep( + &nextReport.channelBinding, + "channel binding channel resource owner or transport resource exchange provenance was not verified"); + } else if (lowerLayerResult.channelBindingInstalled) { + MarkMissingStep(&nextReport.channelBinding, "channel binding endpoint route provenance was not verified"); + } else { + MarkMissingStep(&nextReport.channelBinding, "channel binding provider is not implemented"); + } + if (request.lowerLayerPlan == nullptr) { + nextReport.message = + "repository instruction image installed via SET_INSTRUCTION; " + + (nextReport.mission.satisfied ? missionProofMessage : nextReport.mission.message) + + "; lower-layer CCU resources are missing"; + } else { + nextReport.message = + "repository instruction image installed via SET_INSTRUCTION; " + lowerLayerResult.message + + "; " + (nextReport.mission.satisfied ? remoteXnProofMessage : + nextReport.mission.message + "; " + remoteXnProofMessage); + } + FillManifestInstallSurfaceCounts(request.manifest, &nextReport); + + if (report != nullptr) { + *report = nextReport; + } + if (TileXRCcuBuildInstallEvidence(nextReport, evidence) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + if (nextReport.missingInstallSurfaceCount == 0) { + return TILEXR_SUCCESS; + } + return TILEXR_ERROR_NOT_FOUND; +} + +} // namespace + +int TileXRCcuBuildInstallEvidence( + const TileXRCcuInstallProviderReport& installReport, + TileXRCcuHardwareInstallEvidence* evidence) +{ + if (evidence == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *evidence = TileXRCcuHardwareInstallEvidence{}; + + if (RejectOfflinePublicVerified(installReport)) { + const std::string reason = "offline install evidence cannot be public verified"; + (void)reason; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallReceipt(installReport) != TILEXR_SUCCESS) { + const std::string reason = "install attempt receipt mismatch"; + (void)reason; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuHardwareInstallEvidence result; + result.missionInstalled = installReport.mission.satisfied; + result.repositoryInstalled = installReport.repository.satisfied; + result.localXnInstalled = installReport.localXn.satisfied; + result.remoteXnBound = installReport.remoteXn.satisfied; + result.notifyCkeInstalled = installReport.notifyCke.satisfied; + result.channelBindingsInstalled = installReport.channelBinding.satisfied; + result.missionSource = SourceOrMissing(installReport.mission, "mission/key install evidence is missing"); + result.repositorySource = SourceOrMissing(installReport.repository, "repository install evidence is missing"); + result.localXnSource = SourceOrMissing(installReport.localXn, "local XN install evidence is missing"); + result.remoteXnSource = SourceOrMissing(installReport.remoteXn, "remote XN binding evidence is missing"); + result.notifyCkeSource = SourceOrMissing(installReport.notifyCke, "notify CKE install evidence is missing"); + result.channelBindingSource = + SourceOrMissing(installReport.channelBinding, "channel binding install evidence is missing"); + + *evidence = result; + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildInstallManifest( + const TileXRCcuLaunchPackage& package, + TileXRCcuInstallManifest* manifest, + TileXRCcuInstallManifestReport* report) +{ + ResetManifestReport(report); + if (manifest == nullptr) { + return FailManifest(nullptr, report, "missing output CCU install manifest"); + } + *manifest = TileXRCcuInstallManifest{}; + + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (ValidateInstallManifestScope(package, packageFingerprint, manifest, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuInstallManifest result; + result.packageFingerprint = packageFingerprint; + result.deviceId = package.installScope.deviceId; + result.rank = package.installScope.rank; + result.provider = package.installScope.provider; + result.requiresHardwareInstall = package.requiresHardwareInstall; + result.installAttemptReceiptRequired = true; + result.requiredEvidenceKind = TileXRCcuEvidenceKind::PublicVerified; + result.requiredEvidenceSurface = TileXRCcuEvidenceSurface::PublicInstallProvider; + + TileXRCcuInstallRequirement mission = BaseRequirement( + TileXRCcuInstallRequirementKind::MissionKey, + packageFingerprint, + package.plan.mission.dieId, + "mission/key", + "install CCU mission id and key"); + mission.missionId = package.plan.mission.missionId; + mission.missionKey = package.plan.mission.key; + mission.resourceStartId = package.plan.kernelLocalMission.startId; + mission.resourceCount = package.plan.kernelLocalMission.num; + result.requirements.push_back(mission); + + TileXRCcuInstallRequirement repository = BaseRequirement( + TileXRCcuInstallRequirementKind::RepositoryImage, + packageFingerprint, + package.repository.dieId, + "repository", + "install generated CCU repository image"); + repository.repositoryStartId = package.repository.repositoryStartId; + repository.repositoryCount = package.repository.repositoryCount; + repository.missionStartId = package.repository.missionStartId; + repository.missionCount = package.repository.missionCount; + repository.instructionCount = static_cast(package.repository.instructions.size()); + result.requirements.push_back(repository); + + TileXRCcuInstallRequirement localXn = BaseRequirement( + TileXRCcuInstallRequirementKind::LocalXn, + packageFingerprint, + package.plan.kernelLocalXn.dieId, + "local XN", + "install kernel-local CCU XN resource window"); + localXn.resourceStartId = package.plan.kernelLocalXn.startId; + localXn.resourceCount = package.plan.kernelLocalXn.num; + result.requirements.push_back(localXn); + + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode)) { + TileXRCcuInstallRequirement remoteXn = BaseRequirement( + TileXRCcuInstallRequirementKind::RemoteXnBinding, + packageFingerprint, + package.plan.kernelLocalXn.dieId, + "remote XN binding", + "bind remote CCU XN resources referenced by sync instructions"); + remoteXn.syncResourceCount = static_cast(package.plan.syncResources.size()); + result.requirements.push_back(remoteXn); + } + + TileXRCcuInstallRequirement notifyCke = BaseRequirement( + TileXRCcuInstallRequirementKind::NotifyCke, + packageFingerprint, + package.plan.kernelLocalCke.dieId, + "notify CKE", + "install notify CKE resources referenced by sync instructions"); + notifyCke.resourceStartId = package.plan.kernelLocalCke.startId; + notifyCke.resourceCount = package.plan.kernelLocalCke.num; + notifyCke.syncResourceCount = static_cast(package.plan.syncResources.size()); + result.requirements.push_back(notifyCke); + + if (RequiresChannelBindingEvidence(package.plan.barrierMode)) { + TileXRCcuInstallRequirement channel = BaseRequirement( + TileXRCcuInstallRequirementKind::ChannelBinding, + packageFingerprint, + package.plan.kernelLocalXn.dieId, + "channel binding", + "bind CCU channel routes for sync resources"); + channel.syncResourceCount = static_cast(package.plan.syncResources.size()); + channel.bindingCount = TotalBindingCount(package.plan.syncResources); + result.requirements.push_back(channel); + } + + if (report != nullptr) { + report->requirementCount = static_cast(result.requirements.size()); + report->message = "ok"; + } + *manifest = result; + return TILEXR_SUCCESS; +} + +int TileXRCcuInstallHardware( + const TileXRCcuInstallRequest& request, + TileXRCcuHardwareInstallEvidence* evidence, + TileXRCcuInstallProviderReport* report) +{ + ResetReport(report); + if (evidence == nullptr) { + FillUnsupportedReport(report); + if (report != nullptr) { + report->message = "missing output CCU hardware install evidence"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *evidence = TileXRCcuHardwareInstallEvidence{}; + + if (request.package == nullptr) { + FillUnsupportedReport(report); + if (report != nullptr) { + report->message = "missing CCU launch package for hardware install"; + } + TileXRCcuInstallProviderReport missingReport; + FillUnsupportedReport(&missingReport); + return TileXRCcuBuildInstallEvidence(missingReport, evidence) == TILEXR_SUCCESS ? + TILEXR_ERROR_PARA_CHECK_FAIL : + TILEXR_ERROR_INTERNAL; + } + + if (ValidateInstallRequestScope(request, report) != TILEXR_SUCCESS) { + TileXRCcuInstallProviderReport missingReport; + FillScopeFailureReport(&missingReport, report == nullptr ? "install request scope mismatch" : report->message); + return TileXRCcuBuildInstallEvidence(missingReport, evidence) == TILEXR_SUCCESS ? + TILEXR_ERROR_PARA_CHECK_FAIL : + TILEXR_ERROR_INTERNAL; + } + if (ValidateInstallRequestManifest(request, report) != TILEXR_SUCCESS) { + TileXRCcuInstallProviderReport missingReport; + FillScopeFailureReport( + &missingReport, + report == nullptr ? "install request manifest mismatch" : report->message); + return TileXRCcuBuildInstallEvidence(missingReport, evidence) == TILEXR_SUCCESS ? + TILEXR_ERROR_PARA_CHECK_FAIL : + TILEXR_ERROR_INTERNAL; + } + + if (HasRepositoryInstallProviderInputs(request)) { + return InstallRepositoryOnly(request, evidence, report); + } + + FillUnsupportedReport(report); + FillManifestInstallSurfaceCounts(request.manifest, report); + TileXRCcuInstallProviderReport missingReport; + FillUnsupportedReport(&missingReport); + if (TileXRCcuBuildInstallEvidence(missingReport, evidence) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + return TILEXR_ERROR_NOT_FOUND; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_install_provider.h b/src/comm/ccu/tilexr_ccu_install_provider.h new file mode 100644 index 0000000..5d12b51 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_install_provider.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_INSTALL_PROVIDER_H +#define TILEXR_CCU_INSTALL_PROVIDER_H + +#include "ccu/tilexr_ccu_provider.h" + +#include +#include +#include + +namespace TileXR { + +enum class TileXRCcuInstallRequirementKind : uint8_t { + MissionKey = 0, + RepositoryImage = 1, + LocalXn = 2, + RemoteXnBinding = 3, + NotifyCke = 4, + ChannelBinding = 5, +}; + +struct TileXRCcuInstallRequirement { + TileXRCcuInstallRequirementKind kind = TileXRCcuInstallRequirementKind::MissionKey; + uint64_t packageFingerprint = 0; + uint8_t dieId = 0; + uint8_t missionId = 0; + uint32_t missionKey = 0; + uint16_t resourceStartId = 0; + uint16_t resourceCount = 0; + uint16_t repositoryStartId = 0; + uint16_t repositoryCount = 0; + uint16_t missionStartId = 0; + uint16_t missionCount = 0; + uint32_t instructionCount = 0; + uint32_t syncResourceCount = 0; + uint32_t bindingCount = 0; + std::string label; + std::string detail; +}; + +struct TileXRCcuInstallManifest { + uint64_t packageFingerprint = 0; + uint32_t deviceId = 0; + uint32_t rank = 0; + std::string provider; + bool requiresHardwareInstall = true; + bool installAttemptReceiptRequired = true; + TileXRCcuEvidenceKind requiredEvidenceKind = TileXRCcuEvidenceKind::PublicVerified; + TileXRCcuEvidenceSurface requiredEvidenceSurface = TileXRCcuEvidenceSurface::PublicInstallProvider; + std::vector requirements; +}; + +struct TileXRCcuInstallManifestReport { + uint32_t requirementCount = 0; + std::string message; +}; + +struct TileXRCcuMsidTokenInstall { + uint8_t dieId = 0; + uint32_t msId = 0; + uint32_t tokenId = 0; + uint32_t tokenValue = 0; +}; + +struct TileXRCcuPfeInstall { + uint8_t dieId = 0; + uint32_t pfeOffset = 0; + TileXRCcuPfeCtx ctx; +}; + +struct TileXRCcuJettyInstall { + uint8_t dieId = 0; + uint16_t startJettyCtxId = 0; + std::vector ctxs; +}; + +struct TileXRCcuChannelInstall { + uint8_t dieId = 0; + uint32_t channelId = 0; + TileXRCcuChannelCtxDataV1 ctx; +}; + +struct TileXRCcuCkeClearInstall { + uint8_t dieId = 0; + uint32_t startCkeId = 0; + uint32_t count = 0; +}; + +struct TileXRCcuXnClearInstall { + uint8_t dieId = 0; + uint32_t startXnId = 0; + uint32_t count = 0; +}; + +struct TileXRCcuRemoteXnBindingProof { + uint8_t dieId = 0; + uint16_t channelId = 0; + uint16_t localXn = 0; + uint16_t remoteXn = 0; + uint16_t notifyCke = 0; + uint32_t peerRank = 0; + bool peerExchangeObserved = false; + uint16_t localWaitCke = 0; + bool endpointRouteVerified = false; + bool channelResourceOwnerVerified = false; + bool transportResourceExchangeVerified = false; +}; + +struct TileXRCcuLowerLayerInstallPlan { + std::vector msidTokens; + std::vector pfes; + std::vector jettys; + std::vector channels; + std::vector xnClears; + std::vector ckeClears; + std::vector remoteXnBindings; +}; + +enum class TileXRCcuInstallOrder : uint8_t { + RepositoryFirst = 0, + InstallLowerLayerFirst = 1, +}; + +struct TileXRCcuInstallRequest { + const TileXRCcuLaunchPackage* package = nullptr; + const TileXRCcuInstallManifest* manifest = nullptr; + uint32_t deviceId = 0; + uint32_t rank = 0; + std::string provider; + bool offlineOnly = true; + const TileXRCcuDriverAdapter* driverAdapter = nullptr; + TileXRCcuDeviceMemoryOps repositoryMemoryOps; + void* repositoryMemoryUserData = nullptr; + TileXRCcuRepositoryInstallOptions repositoryInstallOptions; + TileXRCcuRepositoryInstallReceipt* repositoryReceipt = nullptr; + TileXRCcuInstallOrder installOrder = TileXRCcuInstallOrder::RepositoryFirst; + const TileXRCcuLowerLayerInstallPlan* lowerLayerPlan = nullptr; +}; + +struct TileXRCcuInstallStepEvidence { + bool satisfied = false; + TileXRCcuEvidenceSource source; + std::string message; +}; + +struct TileXRCcuInstallProviderReport { + TileXRCcuInstallStepEvidence mission; + TileXRCcuInstallStepEvidence repository; + TileXRCcuInstallStepEvidence localXn; + TileXRCcuInstallStepEvidence remoteXn; + TileXRCcuInstallStepEvidence notifyCke; + TileXRCcuInstallStepEvidence channelBinding; + bool offlineOnly = true; + bool installAttempted = false; + bool installSucceeded = false; + uint32_t requiredInstallSurfaceCount = 0; + uint32_t publicVerifiedInstallSurfaceCount = 0; + uint32_t missingInstallSurfaceCount = 0; + uint64_t installAttemptReceiptId = 0; + std::string message; +}; + +int TileXRCcuBuildInstallEvidence( + const TileXRCcuInstallProviderReport& installReport, + TileXRCcuHardwareInstallEvidence* evidence); + +int TileXRCcuBuildInstallManifest( + const TileXRCcuLaunchPackage& package, + TileXRCcuInstallManifest* manifest, + TileXRCcuInstallManifestReport* report); + +int TileXRCcuInstallHardware( + const TileXRCcuInstallRequest& request, + TileXRCcuHardwareInstallEvidence* evidence, + TileXRCcuInstallProviderReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_INSTALL_PROVIDER_H diff --git a/src/comm/ccu/tilexr_ccu_launch_package.cpp b/src/comm/ccu/tilexr_ccu_launch_package.cpp new file mode 100644 index 0000000..fa1a5ec --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_launch_package.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_launch_package.h" + +namespace TileXR { +namespace { + +constexpr uint64_t TILEXR_CCU_FNV_OFFSET = 1469598103934665603ULL; +constexpr uint64_t TILEXR_CCU_FNV_PRIME = 1099511628211ULL; + +void MixByte(uint8_t value, uint64_t* hash) +{ + *hash ^= value; + *hash *= TILEXR_CCU_FNV_PRIME; +} + +void MixU16(uint16_t value, uint64_t* hash) +{ + MixByte(static_cast(value & 0xffU), hash); + MixByte(static_cast((value >> 8U) & 0xffU), hash); +} + +void MixU32(uint32_t value, uint64_t* hash) +{ + MixU16(static_cast(value & 0xffffU), hash); + MixU16(static_cast((value >> 16U) & 0xffffU), hash); +} + +void MixU64(uint64_t value, uint64_t* hash) +{ + MixU32(static_cast(value & 0xffffffffULL), hash); + MixU32(static_cast((value >> 32ULL) & 0xffffffffULL), hash); +} + +void MixRange(const TileXRCcuRange& range, uint64_t* hash) +{ + MixByte(range.dieId, hash); + MixU16(range.startId, hash); + MixU16(range.num, hash); +} + +void MixMission(const TileXRCcuMission& mission, uint64_t* hash) +{ + MixByte(mission.dieId, hash); + MixByte(mission.missionId, hash); + MixU32(mission.key, hash); + MixByte(mission.installed ? 1U : 0U, hash); +} + +void MixInstructionWindow(const TileXRCcuInstructionWindow& window, uint64_t* hash) +{ + MixByte(window.dieId, hash); + MixU16(window.repositoryStartId, hash); + MixU16(window.repositoryCount, hash); + MixU16(window.missionStartId, hash); + MixU16(window.missionCount, hash); +} + +void MixSyncResource(const TileXRCcuSyncResource& resource, uint64_t* hash) +{ + MixByte(resource.dieId, hash); + MixU16(resource.localXn, hash); + MixU16(resource.remoteXn, hash); + MixU16(resource.notifyCke, hash); + MixU16(resource.channelId, hash); + MixU16(resource.bindingCount, hash); + MixU16(resource.localWaitCke, hash); + MixU16(resource.localWaitMask, hash); + MixU16(resource.remoteNotifyMask, hash); + MixU16(resource.sourceCke, hash); + MixU16(resource.sourceCkeMask, hash); +} + +void MixTaskWindow(const TileXRCcuTaskWindow& window, uint64_t* hash) +{ + MixByte(window.dieId, hash); + MixU16(window.instStartId, hash); + MixU16(window.instCnt, hash); + MixU32(window.argSize, hash); + MixU64(static_cast(window.args.size()), hash); + for (uint64_t arg : window.args) { + MixU64(arg, hash); + } +} + +void MixInstr(const TileXRCcuInstr& instr, uint64_t* hash) +{ + for (uint64_t word : instr.words) { + MixU64(word, hash); + } +} + +void MixRepository(const TileXRCcuRepositoryImage& repository, uint64_t* hash) +{ + MixByte(repository.dieId, hash); + MixU16(repository.repositoryStartId, hash); + MixU16(repository.repositoryCount, hash); + MixU16(repository.missionStartId, hash); + MixU16(repository.missionCount, hash); + MixU16(repository.missionOffset, hash); + MixU16(repository.sqeLoadOffset, hash); + MixU16(repository.sqeLoadCount, hash); + MixU16(repository.syncOffset, hash); + MixU16(repository.syncCount, hash); + MixU64(static_cast(repository.instructions.size()), hash); + for (const auto& instr : repository.instructions) { + MixInstr(instr, hash); + } +} + +void MixTask(const TileXRCcuTask& task, uint64_t* hash) +{ + MixByte(task.dieId, hash); + MixByte(task.missionId, hash); + MixU16(task.timeout, hash); + MixU16(task.instStartId, hash); + MixU16(task.instCnt, hash); + MixU32(task.key, hash); + MixU32(task.argSize, hash); + for (uint64_t arg : task.args) { + MixU64(arg, hash); + } +} + +void ResetReport(TileXRCcuLaunchPackageReport* report) +{ + if (report == nullptr) { + return; + } + report->taskCount = 0; + report->repositoryCount = 0; + report->installedInstructionCount = 0; + report->message.clear(); +} + +void ClearPackage(TileXRCcuLaunchPackage* package) +{ + if (package == nullptr) { + return; + } + package->plan = TileXRCcuProducerPlan{}; + package->program = TileXRCcuProgram{}; + package->repository = TileXRCcuRepositoryImage{}; + package->tasks.clear(); + package->installScope = TileXRCcuLaunchInstallScope{}; + package->requiresHardwareInstall = true; +} + +int Fail(TileXRCcuLaunchPackageReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +void FillReport(const TileXRCcuLaunchPackage& package, TileXRCcuLaunchPackageReport* report) +{ + if (report == nullptr) { + return; + } + report->taskCount = static_cast(package.tasks.size()); + report->repositoryCount = package.repository.repositoryCount; + report->installedInstructionCount = + static_cast(package.repository.sqeLoadCount + package.repository.syncCount); + report->message = "ok"; +} + +} // namespace + +int TileXRCcuBuildLaunchPackage( + const TileXRCcuProducerPlan& plan, + TileXRCcuLaunchPackage* package, + TileXRCcuLaunchPackageReport* report) +{ + ResetReport(report); + if (package == nullptr) { + return Fail(report, "missing output CCU launch package"); + } + ClearPackage(package); + + TileXRCcuProducerPlanReport planReport; + TileXRCcuProgram program; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + ClearPackage(package); + return Fail(report, planReport.message); + } + + TileXRCcuRepositoryImage repository; + TileXRCcuRepositoryReport repositoryReport; + if (TileXRCcuBuildRepositoryImage(plan, program, &repository, &repositoryReport) != TILEXR_SUCCESS) { + ClearPackage(package); + return Fail(report, repositoryReport.message); + } + + std::vector tasks; + if (TileXRCcuBuildTasks(plan, &tasks, &planReport) != TILEXR_SUCCESS) { + ClearPackage(package); + return Fail(report, planReport.message); + } + + package->plan = plan; + package->program = program; + package->repository = repository; + package->tasks = tasks; + package->installScope = TileXRCcuLaunchInstallScope{}; + package->requiresHardwareInstall = true; + FillReport(*package, report); + return TILEXR_SUCCESS; +} + +uint64_t TileXRCcuComputeLaunchPackageFingerprint(const TileXRCcuLaunchPackage& package) +{ + uint64_t hash = TILEXR_CCU_FNV_OFFSET; + MixU64(0x54494c4558524343ULL, &hash); // "TILEXRCC" + MixMission(package.plan.mission, &hash); + MixRange(package.plan.kernelLocalXn, &hash); + MixRange(package.plan.kernelLocalGsa, &hash); + MixRange(package.plan.kernelLocalCke, &hash); + MixRange(package.plan.kernelLocalMission, &hash); + MixU32(static_cast(package.plan.barrierMode), &hash); + MixInstructionWindow(package.plan.instructionWindow, &hash); + MixU64(static_cast(package.plan.syncResources.size()), &hash); + for (const auto& resource : package.plan.syncResources) { + MixSyncResource(resource, &hash); + } + MixU64(static_cast(package.plan.taskWindows.size()), &hash); + for (const auto& window : package.plan.taskWindows) { + MixTaskWindow(window, &hash); + } + MixU64(static_cast(package.program.sqeLoad.size()), &hash); + for (const auto& instr : package.program.sqeLoad) { + MixInstr(instr, &hash); + } + MixU64(static_cast(package.program.sync.size()), &hash); + for (const auto& instr : package.program.sync) { + MixInstr(instr, &hash); + } + MixRepository(package.repository, &hash); + MixU64(static_cast(package.tasks.size()), &hash); + for (const auto& task : package.tasks) { + MixTask(task, &hash); + } + MixByte(package.requiresHardwareInstall ? 1U : 0U, &hash); + return hash == 0 ? 1 : hash; +} + +int TileXRCcuBindLaunchPackageInstallScope( + TileXRCcuLaunchPackage* package, + uint32_t deviceId, + uint32_t rank, + const std::string& provider) +{ + if (package == nullptr || provider.empty()) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (package->tasks.empty() || package->repository.instructions.empty()) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + package->installScope.deviceId = deviceId; + package->installScope.rank = rank; + package->installScope.provider = provider; + package->installScope.packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(*package); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_launch_package.h b/src/comm/ccu/tilexr_ccu_launch_package.h new file mode 100644 index 0000000..891b4d4 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_launch_package.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_LAUNCH_PACKAGE_H +#define TILEXR_CCU_LAUNCH_PACKAGE_H + +#include "ccu/tilexr_ccu_repository.h" + +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuLaunchInstallScope { + uint32_t deviceId = 0; + uint32_t rank = 0; + uint64_t packageFingerprint = 0; + std::string provider; +}; + +struct TileXRCcuLaunchPackage { + TileXRCcuProducerPlan plan; + TileXRCcuProgram program; + TileXRCcuRepositoryImage repository; + std::vector tasks; + TileXRCcuLaunchInstallScope installScope; + bool requiresHardwareInstall = true; +}; + +struct TileXRCcuLaunchPackageReport { + uint32_t taskCount = 0; + uint32_t repositoryCount = 0; + uint32_t installedInstructionCount = 0; + std::string message; +}; + +int TileXRCcuBuildLaunchPackage( + const TileXRCcuProducerPlan& plan, + TileXRCcuLaunchPackage* package, + TileXRCcuLaunchPackageReport* report); + +uint64_t TileXRCcuComputeLaunchPackageFingerprint(const TileXRCcuLaunchPackage& package); + +int TileXRCcuBindLaunchPackageInstallScope( + TileXRCcuLaunchPackage* package, + uint32_t deviceId, + uint32_t rank, + const std::string& provider); + +} // namespace TileXR + +#endif // TILEXR_CCU_LAUNCH_PACKAGE_H diff --git a/src/comm/ccu/tilexr_ccu_lower_layer_payloads.cpp b/src/comm/ccu/tilexr_ccu_lower_layer_payloads.cpp new file mode 100644 index 0000000..8256d6b --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_payloads.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_lower_layer_payloads.h" + +#include +#include + +namespace TileXR { +namespace { + +constexpr uint32_t TOKEN_VALUE_VALID = 1; +constexpr uint32_t DOORBELL_ADDR_TYPE_VA = 1; +constexpr uint32_t DOORBELL_TOKEN_VALUE_VALID = 1; +constexpr uint32_t CCU_WQE_NUM_PER_SQE = 4; + +void ResetReport(TileXRCcuLowerLayerPayloadReport* report) +{ + if (report != nullptr) { + report->message.clear(); + } +} + +int Fail(TileXRCcuLowerLayerPayloadReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +void Store16(uint8_t* raw, uint32_t offset, uint16_t value) +{ + raw[offset] = static_cast(value & 0xffU); + raw[offset + 1U] = static_cast((value >> 8U) & 0xffU); +} + +uint16_t Log2PowerOfTwo(uint32_t value) +{ + uint16_t log2 = 0; + while (value > 1U) { + value >>= 1U; + ++log2; + } + return log2; +} + +bool IsPowerOfTwo(uint32_t value) +{ + return value != 0 && (value & (value - 1U)) == 0; +} + +bool IsEidEmpty(const std::array& eid) +{ + return std::all_of(eid.begin(), eid.end(), [](uint8_t byte) { return byte == 0; }); +} + +} // namespace + +int TileXRCcuBuildPfeCtx( + const TileXRCcuPfeCtxSpec& spec, + TileXRCcuPfeCtx* ctx, + TileXRCcuLowerLayerPayloadReport* report) +{ + ResetReport(report); + if (ctx == nullptr) { + return Fail(report, "missing output CCU PFE context"); + } + std::memset(ctx->raw, 0, sizeof(ctx->raw)); + if (spec.startJettyId == 0 || spec.jettyCount == 0 || spec.jettyCount > 128U || + spec.startLocalJettyCtxId >= 128U) { + return Fail(report, "invalid CCU PFE context spec"); + } + + Store16(ctx->raw, 0, spec.startJettyId); + const uint16_t word = static_cast( + ((spec.jettyCount - 1U) & 0x7fU) | + ((static_cast(spec.startLocalJettyCtxId) & 0x7fU) << 7U)); + Store16(ctx->raw, 2, word); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildLocalJettyCtx( + const TileXRCcuLocalJettyCtxSpec& spec, + TileXRCcuLocalJettyCtxData* ctx, + TileXRCcuLowerLayerPayloadReport* report) +{ + ResetReport(report); + if (ctx == nullptr) { + return Fail(report, "missing output CCU local jetty context"); + } + std::memset(ctx->raw, 0, sizeof(ctx->raw)); + const uint32_t wqeBasicBlocks = spec.sqDepth * CCU_WQE_NUM_PER_SQE; + if (spec.pfeId > 0xfU || spec.dieId > 1U || spec.doorbellVa == 0 || + spec.sqDepth == 0 || !IsPowerOfTwo(wqeBasicBlocks)) { + return Fail(report, "invalid CCU local jetty context spec"); + } + + Store16(ctx->raw, 0, static_cast(spec.doorbellVa & 0xffffU)); + Store16(ctx->raw, 2, static_cast((spec.doorbellVa >> 16U) & 0xffffU)); + Store16(ctx->raw, 4, static_cast((spec.doorbellVa >> 32U) & 0xffffU)); + Store16(ctx->raw, 6, static_cast((spec.doorbellVa >> 48U) & 0xffffU)); + + Store16(ctx->raw, 8, static_cast( + (spec.pfeId & 0xfU) | + ((static_cast(spec.dieId) & 0x1U) << 4U) | + (DOORBELL_ADDR_TYPE_VA << 5U) | + (DOORBELL_TOKEN_VALUE_VALID << 6U) | + ((spec.doorbellTokenId & 0xffU) << 8U))); + Store16(ctx->raw, 10, static_cast( + ((spec.doorbellTokenId >> 8U) & 0xfffU) | + ((spec.doorbellTokenValue & 0xfU) << 12U))); + Store16(ctx->raw, 12, static_cast((spec.doorbellTokenValue >> 4U) & 0xffffU)); + Store16(ctx->raw, 14, static_cast( + ((spec.doorbellTokenValue >> 20U) & 0xfffU) | + ((static_cast(Log2PowerOfTwo(wqeBasicBlocks)) & 0xfU) << 12U))); + Store16(ctx->raw, 22, static_cast( + (static_cast(spec.wqeBasicBlockStartId) & 0xfU) << 12U)); + Store16(ctx->raw, 24, static_cast((spec.wqeBasicBlockStartId >> 4U) & 0xffU)); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildChannelCtxV1( + const TileXRCcuChannelCtxV1Spec& spec, + TileXRCcuChannelCtxDataV1* ctx, + TileXRCcuLowerLayerPayloadReport* report) +{ + ResetReport(report); + if (ctx == nullptr) { + return Fail(report, "missing output CCU channel context v1"); + } + std::memset(ctx->raw, 0, sizeof(ctx->raw)); + if (IsEidEmpty(spec.remoteEid) || spec.sourcePfeId > 0xfU || spec.startJettyId == 0 || + spec.jettyCount == 0 || spec.jettyCount > 128U || spec.dieId > 1U || + spec.remoteCcuVa == 0) { + return Fail(report, "invalid CCU channel context v1 spec"); + } + + std::copy(spec.remoteEid.begin(), spec.remoteEid.end(), ctx->raw); + Store16(ctx->raw, 16, static_cast(spec.tpn & 0xffffU)); + Store16(ctx->raw, 18, static_cast( + ((spec.tpn >> 16U) & 0xffU) | + ((spec.sourcePfeId & 0xfU) << 8U) | + ((static_cast(spec.startJettyId) & 0xfU) << 12U))); + const uint32_t jettyNumMinusOne = spec.jettyCount - 1U; + Store16(ctx->raw, 20, static_cast( + ((static_cast(spec.startJettyId) >> 4U) & 0xfffU) | + ((jettyNumMinusOne & 0xfU) << 12U))); + Store16(ctx->raw, 22, static_cast( + ((jettyNumMinusOne >> 4U) & 0x7U) | + ((static_cast(spec.dieId) & 0x1U) << 3U) | + ((spec.memoryTokenId & 0xfffU) << 4U))); + Store16(ctx->raw, 24, static_cast( + ((spec.memoryTokenId >> 12U) & 0xffU) | + ((spec.memoryTokenValue & 0xffU) << 8U))); + Store16(ctx->raw, 26, static_cast((spec.memoryTokenValue >> 8U) & 0xffffU)); + + const uint64_t dstVa = spec.remoteCcuVa >> TILEXR_CCU_REMOTE_CCU_VA_SHIFT; + Store16(ctx->raw, 28, static_cast( + ((spec.memoryTokenValue >> 24U) & 0xffU) | + ((dstVa & 0xffU) << 8U))); + Store16(ctx->raw, 30, static_cast((dstVa >> 8U) & 0xffffU)); + Store16(ctx->raw, 32, static_cast((dstVa >> 24U) & 0xffffU)); + Store16(ctx->raw, 34, static_cast( + ((dstVa >> 40U) & 0x1U) | + (TOKEN_VALUE_VALID << 1U))); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_lower_layer_payloads.h b/src/comm/ccu/tilexr_ccu_lower_layer_payloads.h new file mode 100644 index 0000000..30fb33f --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_payloads.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_LOWER_LAYER_PAYLOADS_H +#define TILEXR_CCU_LOWER_LAYER_PAYLOADS_H + +#include "ccu/tilexr_ccu_abi_constants.h" +#include "ccu/tilexr_ccu_driver_adapter.h" + +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuPfeCtxSpec { + uint16_t startJettyId = 0; + uint16_t jettyCount = 0; + uint16_t startLocalJettyCtxId = 0; +}; + +struct TileXRCcuLocalJettyCtxSpec { + uint8_t dieId = 0; + uint32_t pfeId = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + uint16_t wqeBasicBlockStartId = 0; +}; + +struct TileXRCcuChannelCtxV1Spec { + std::array remoteEid {}; + uint32_t tpn = 0; + uint32_t sourcePfeId = 0; + uint16_t startJettyId = 0; + uint16_t jettyCount = 0; + uint8_t dieId = 0; + uint32_t memoryTokenId = 0; + uint32_t memoryTokenValue = 0; + uint64_t remoteCcuVa = 0; +}; + +struct TileXRCcuLowerLayerPayloadReport { + std::string message; +}; + +int TileXRCcuBuildPfeCtx( + const TileXRCcuPfeCtxSpec& spec, + TileXRCcuPfeCtx* ctx, + TileXRCcuLowerLayerPayloadReport* report); + +int TileXRCcuBuildLocalJettyCtx( + const TileXRCcuLocalJettyCtxSpec& spec, + TileXRCcuLocalJettyCtxData* ctx, + TileXRCcuLowerLayerPayloadReport* report); + +int TileXRCcuBuildChannelCtxV1( + const TileXRCcuChannelCtxV1Spec& spec, + TileXRCcuChannelCtxDataV1* ctx, + TileXRCcuLowerLayerPayloadReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_LOWER_LAYER_PAYLOADS_H diff --git a/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.cpp b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.cpp new file mode 100644 index 0000000..095c38a --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.cpp @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + +#include +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr uint16_t TILEXR_CCU_DEFAULT_START_JETTY_ID = 1024; +constexpr uint16_t TILEXR_CCU_DEFAULT_START_LOCAL_JETTY_CTX_ID = 0; +constexpr uint16_t TILEXR_CCU_WQE_BASIC_BLOCKS_PER_ROUTE = 4; +constexpr uint16_t TILEXR_CCU_HCOMM_WQE_BASIC_BLOCKS_PER_ROUTE = 256; +constexpr uint32_t TILEXR_CCU_HCOMM_PER_DIE_PFE_RESERVED_NUM = 16; +constexpr uint16_t TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM = 23; +constexpr uint16_t TILEXR_CCU_HCOMM_OUTER_FE_START_JETTY_CTX_ID = 92; +constexpr uint16_t TILEXR_CCU_HCOMM_OUTER_FE_JETTY_NUM = 36; +constexpr uint32_t TILEXR_CCU_HCOMM_MAX_INNER_FE_ID = 7; + +void ResetReport(TileXRCcuLowerLayerPlanBuilderReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuLowerLayerPlanBuilderReport{}; + } +} + +int Fail( + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + const std::string& message) +{ + if (plan != nullptr) { + *plan = TileXRCcuLowerLayerInstallPlan{}; + } + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +int FailPayload( + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + const TileXRCcuLowerLayerPayloadReport& payloadReport) +{ + return Fail(plan, report, payloadReport.message.empty() ? "invalid lower-layer CCU payload spec" : + payloadReport.message); +} + +uint16_t CheckedU16(uint32_t value) +{ + return static_cast(std::min(value, std::numeric_limits::max())); +} + +bool AddOverflowsU16(uint16_t start, uint32_t count) +{ + return static_cast(start) + count > + static_cast(std::numeric_limits::max()) + 1U; +} + +TileXRCcuRange EffectiveLocalWaitCkeRange(const TileXRCcuResourceAllocation& allocation) +{ + TileXRCcuRange local = allocation.localWaitCke.num == 0 ? allocation.notifyCke : allocation.localWaitCke; + if (allocation.sourceCke.num == 0) { + return local; + } + if (local.dieId == allocation.sourceCke.dieId && + static_cast(local.startId) + local.num == allocation.sourceCke.startId) { + local.num = CheckedU16(static_cast(local.num) + allocation.sourceCke.num); + } + return local; +} + +TileXRCcuRange EffectiveRemoteNotifyCkeRange(const TileXRCcuResourceAllocation& allocation) +{ + return allocation.remoteNotifyCke.num == 0 ? allocation.notifyCke : allocation.remoteNotifyCke; +} + +uint16_t SelectLowerLayerWqeBasicBlockStride() +{ + const char* value = std::getenv("TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE"); + if (value != nullptr && std::strcmp(value, "hcomm_cap") == 0) { + return TILEXR_CCU_HCOMM_WQE_BASIC_BLOCKS_PER_ROUTE; + } + return TILEXR_CCU_WQE_BASIC_BLOCKS_PER_ROUTE; +} + +bool LowerLayerEnvEquals(const char* name, const char* expected) +{ + const char* value = std::getenv(name); + return value != nullptr && std::strcmp(value, expected) == 0; +} + +bool IsEmptyEndpointEid(const std::array& eid) +{ + return std::all_of(eid.begin(), eid.end(), [](uint8_t value) { + return value == 0; + }); +} + +bool HasCompleteVerifiedEndpointRoute(const TileXRCcuLowerLayerTransportRoute& route) +{ + return route.endpointRouteVerified && + !IsEmptyEndpointEid(route.remoteEid) && + route.doorbellVa != 0 && + route.doorbellTokenId != 0 && + route.sqDepth != 0; +} + +bool RangeContains(uint32_t start, uint32_t count, uint32_t value) +{ + return count != 0 && value >= start && value < start + count; +} + +uint32_t SelectLowerLayerPfeOffset(uint8_t dieId, uint32_t pfeId) +{ + if (LowerLayerEnvEquals("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE", "hcomm_die")) { + return static_cast(dieId) * TILEXR_CCU_HCOMM_PER_DIE_PFE_RESERVED_NUM + pfeId; + } + return pfeId; +} + +void ApplyHcommFeIdPfePartition(uint32_t pfeId, TileXRCcuLowerLayerTransportSnapshot* snapshot) +{ + if (snapshot == nullptr) { + return; + } + if (pfeId > TILEXR_CCU_HCOMM_MAX_INNER_FE_ID) { + snapshot->startLocalJettyCtxId = TILEXR_CCU_HCOMM_OUTER_FE_START_JETTY_CTX_ID; + snapshot->pfeJettyCount = TILEXR_CCU_HCOMM_OUTER_FE_JETTY_NUM; + } else { + snapshot->startLocalJettyCtxId = CheckedU16(pfeId * TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM); + snapshot->pfeJettyCount = TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM; + } + snapshot->startJettyId = CheckedU16(TILEXR_CCU_DEFAULT_START_JETTY_ID + snapshot->startLocalJettyCtxId); +} + +void ApplyHcommOrderedPfePartition(TileXRCcuLowerLayerTransportSnapshot* snapshot) +{ + if (snapshot == nullptr) { + return; + } + snapshot->startLocalJettyCtxId = 0; + snapshot->pfeJettyCount = TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM; + snapshot->startJettyId = TILEXR_CCU_DEFAULT_START_JETTY_ID; +} + +void ApplyLowerLayerPfePartition(uint32_t pfeId, TileXRCcuLowerLayerTransportSnapshot* snapshot) +{ + if (LowerLayerEnvEquals("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION", "hcomm_fe_id")) { + ApplyHcommFeIdPfePartition(pfeId, snapshot); + return; + } + if (LowerLayerEnvEquals("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION", "hcomm_ordered") || + LowerLayerEnvEquals("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION", "hcomm")) { + ApplyHcommOrderedPfePartition(snapshot); + } +} + +int ValidateSpec( + const TileXRCcuLowerLayerPlanSpec& spec, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + if (!spec.msidToken.valid) { + return Fail(plan, report, "missing lower-layer CCU MSID token"); + } + if (spec.jettys.empty()) { + return Fail(plan, report, "missing lower-layer CCU local jetty contexts"); + } + if (spec.channels.empty()) { + return Fail(plan, report, "missing lower-layer CCU channel contexts"); + } + if (!spec.xnClear.valid || spec.xnClear.count == 0) { + return Fail(plan, report, "missing lower-layer CCU local XN clear range"); + } + if (!spec.ckeClear.valid || spec.ckeClear.count == 0) { + return Fail(plan, report, "missing lower-layer CCU CKE clear range"); + } + if (spec.jettys.size() > std::numeric_limits::max()) { + return Fail(plan, report, "too many lower-layer CCU local jetty contexts"); + } + if (spec.pfe.jettyCount != 0 && spec.pfe.jettyCount > 128U) { + return Fail(plan, report, "lower-layer CCU PFE jetty count is out of range"); + } + if (spec.pfe.jettyCount != 0 && spec.pfe.jettyCount < spec.jettys.size()) { + return Fail(plan, report, "lower-layer CCU PFE jetty window is smaller than local jetty contexts"); + } + if (AddOverflowsU16(spec.pfe.startLocalJettyCtxId, static_cast(spec.jettys.size()))) { + return Fail(plan, report, "lower-layer CCU local jetty context range overflows"); + } + return TILEXR_SUCCESS; +} + +void FillReport(const TileXRCcuLowerLayerInstallPlan& plan, TileXRCcuLowerLayerPlanBuilderReport* report) +{ + if (report == nullptr) { + return; + } + report->msidTokenCount = static_cast(plan.msidTokens.size()); + report->pfeCount = static_cast(plan.pfes.size()); + report->jettyCount = static_cast(plan.jettys.size()); + report->localJettyCtxCount = 0; + for (const auto& jetty : plan.jettys) { + report->localJettyCtxCount += static_cast(jetty.ctxs.size()); + } + report->channelCount = static_cast(plan.channels.size()); + report->ckeClearCount = static_cast(plan.ckeClears.size()); + report->message = "ok"; +} + +void FillTemplateReport( + const TileXRCcuLowerLayerTransportSnapshot& snapshot, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + if (report == nullptr) { + return; + } + report->msidTokenCount = snapshot.msidToken.valid ? 1U : 0U; + report->pfeCount = snapshot.routes.empty() ? 0U : 1U; + report->jettyCount = static_cast(snapshot.routes.size()); + report->localJettyCtxCount = static_cast(snapshot.routes.size()); + report->channelCount = static_cast(snapshot.routes.size()); + report->ckeClearCount = snapshot.ckeCount == 0 ? 0U : 1U; + report->message = "ok"; +} + +void AppendRemoteNotifyCkeClears( + const TileXRCcuLowerLayerTransportSnapshot& snapshot, + TileXRCcuLowerLayerInstallPlan* plan) +{ + if (plan == nullptr) { + return; + } + std::vector notifyCkes; + notifyCkes.reserve(snapshot.routes.size()); + for (const auto& route : snapshot.routes) { + if (route.remoteNotifyCke == 0 || + RangeContains(snapshot.ckeStartId, snapshot.ckeCount, route.remoteNotifyCke)) { + continue; + } + notifyCkes.push_back(route.remoteNotifyCke); + } + if (notifyCkes.empty()) { + return; + } + + std::sort(notifyCkes.begin(), notifyCkes.end()); + notifyCkes.erase(std::unique(notifyCkes.begin(), notifyCkes.end()), notifyCkes.end()); + + uint32_t rangeStart = notifyCkes.front(); + uint32_t previous = rangeStart; + for (size_t i = 1; i <= notifyCkes.size(); ++i) { + if (i < notifyCkes.size() && notifyCkes[i] == previous + 1U) { + previous = notifyCkes[i]; + continue; + } + plan->ckeClears.push_back({ + snapshot.dieId, + rangeStart, + previous - rangeStart + 1U, + }); + if (i < notifyCkes.size()) { + rangeStart = notifyCkes[i]; + previous = rangeStart; + } + } +} + +void AppendRemoteXnClears( + const TileXRCcuLowerLayerTransportSnapshot& snapshot, + TileXRCcuLowerLayerInstallPlan* plan) +{ + if (plan == nullptr) { + return; + } + std::vector remoteXns; + remoteXns.reserve(snapshot.routes.size()); + for (const auto& route : snapshot.routes) { + if (route.remoteXnId == 0 || + RangeContains(snapshot.xnStartId, snapshot.xnCount, route.remoteXnId)) { + continue; + } + remoteXns.push_back(route.remoteXnId); + } + if (remoteXns.empty()) { + return; + } + + std::sort(remoteXns.begin(), remoteXns.end()); + remoteXns.erase(std::unique(remoteXns.begin(), remoteXns.end()), remoteXns.end()); + + uint32_t rangeStart = remoteXns.front(); + uint32_t previous = rangeStart; + for (size_t i = 1; i <= remoteXns.size(); ++i) { + if (i < remoteXns.size() && remoteXns[i] == previous + 1U) { + previous = remoteXns[i]; + continue; + } + plan->xnClears.push_back({ + snapshot.dieId, + rangeStart, + previous - rangeStart + 1U, + }); + if (i < remoteXns.size()) { + rangeStart = remoteXns[i]; + previous = rangeStart; + } + } +} + +} // namespace + +int TileXRCcuBuildLowerLayerInstallPlan( + const TileXRCcuLowerLayerPlanSpec& spec, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + ResetReport(report); + if (plan == nullptr) { + return Fail(nullptr, report, "missing output lower-layer CCU install plan"); + } + *plan = TileXRCcuLowerLayerInstallPlan{}; + + if (ValidateSpec(spec, plan, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuLowerLayerInstallPlan result; + result.msidTokens.push_back({ + spec.msidToken.dieId, + spec.msidToken.msId, + spec.msidToken.tokenId, + spec.msidToken.tokenValue, + }); + + TileXRCcuLowerLayerPayloadReport payloadReport; + TileXRCcuPfeInstall pfe; + pfe.dieId = spec.pfe.dieId; + pfe.pfeOffset = spec.pfe.pfeOffset; + TileXRCcuPfeCtxSpec pfeCtxSpec; + pfeCtxSpec.startJettyId = spec.pfe.startJettyId; + pfeCtxSpec.jettyCount = spec.pfe.jettyCount == 0 ? + CheckedU16(static_cast(spec.jettys.size())) : + spec.pfe.jettyCount; + pfeCtxSpec.startLocalJettyCtxId = spec.pfe.startLocalJettyCtxId; + if (TileXRCcuBuildPfeCtx(pfeCtxSpec, &pfe.ctx, &payloadReport) != TILEXR_SUCCESS) { + return FailPayload(plan, report, payloadReport); + } + result.pfes.push_back(pfe); + + TileXRCcuJettyInstall jettyInstall; + jettyInstall.dieId = spec.pfe.dieId; + jettyInstall.startJettyCtxId = spec.pfe.startLocalJettyCtxId; + for (const auto& jettySpec : spec.jettys) { + if (jettySpec.startJettyCtxId != 0 && jettySpec.startJettyCtxId != jettyInstall.startJettyCtxId + + jettyInstall.ctxs.size()) { + return Fail(plan, report, "lower-layer CCU local jetty contexts must be contiguous"); + } + TileXRCcuLocalJettyCtxData ctx; + TileXRCcuLocalJettyCtxSpec ctxSpec; + ctxSpec.dieId = jettySpec.dieId; + ctxSpec.pfeId = jettySpec.pfeId; + ctxSpec.doorbellVa = jettySpec.doorbellVa; + ctxSpec.doorbellTokenId = jettySpec.doorbellTokenId; + ctxSpec.doorbellTokenValue = jettySpec.doorbellTokenValue; + ctxSpec.sqDepth = jettySpec.sqDepth; + ctxSpec.wqeBasicBlockStartId = jettySpec.wqeBasicBlockStartId; + if (TileXRCcuBuildLocalJettyCtx(ctxSpec, &ctx, &payloadReport) != TILEXR_SUCCESS) { + return FailPayload(plan, report, payloadReport); + } + jettyInstall.ctxs.push_back(ctx); + } + result.jettys.push_back(jettyInstall); + + for (const auto& channelSpec : spec.channels) { + TileXRCcuChannelInstall channel; + channel.dieId = channelSpec.dieId; + channel.channelId = channelSpec.channelId; + TileXRCcuChannelCtxV1Spec ctxSpec; + ctxSpec.remoteEid = channelSpec.remoteEid; + ctxSpec.tpn = channelSpec.tpn; + ctxSpec.sourcePfeId = channelSpec.sourcePfeId; + ctxSpec.startJettyId = channelSpec.startJettyId; + ctxSpec.jettyCount = channelSpec.jettyCount == 0 ? + CheckedU16(static_cast(spec.jettys.size())) : + channelSpec.jettyCount; + ctxSpec.dieId = channelSpec.dieId; + ctxSpec.memoryTokenId = channelSpec.memoryTokenId; + ctxSpec.memoryTokenValue = channelSpec.memoryTokenValue; + ctxSpec.remoteCcuVa = channelSpec.remoteCcuVa; + if (TileXRCcuBuildChannelCtxV1(ctxSpec, &channel.ctx, &payloadReport) != TILEXR_SUCCESS) { + return FailPayload(plan, report, payloadReport); + } + result.channels.push_back(channel); + } + + result.xnClears.push_back({ + spec.xnClear.dieId, + spec.xnClear.startXnId, + spec.xnClear.count, + }); + + result.ckeClears.push_back({ + spec.ckeClear.dieId, + spec.ckeClear.startCkeId, + spec.ckeClear.count, + }); + result.remoteXnBindings = spec.remoteXnBindings; + + *plan = result; + FillReport(*plan, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildLowerLayerTransportTemplate( + const TileXRCcuBasicInfo& basicInfo, + const TileXRCcuResourceAllocation& allocation, + const std::vector& remoteCcuBuffers, + TileXRCcuLowerLayerTransportSnapshot* snapshot, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + ResetReport(report); + if (snapshot == nullptr) { + return Fail(nullptr, report, "missing output lower-layer CCU transport template"); + } + *snapshot = TileXRCcuLowerLayerTransportSnapshot{}; + + const TileXRCcuRange localWaitCke = EffectiveLocalWaitCkeRange(allocation); + const TileXRCcuRange remoteNotifyCke = EffectiveRemoteNotifyCkeRange(allocation); + if (allocation.channels.num == 0 || allocation.localXn.num == 0 || localWaitCke.num == 0 || + remoteNotifyCke.num == 0 || allocation.remoteXn.num == 0) { + return Fail(nullptr, report, "missing lower-layer CCU allocated resources"); + } + if (remoteCcuBuffers.empty() || remoteCcuBuffers.size() != allocation.remoteXn.num) { + return Fail(nullptr, report, "remote CCU buffer template count does not match remote XN allocation"); + } + if (allocation.channels.num < remoteCcuBuffers.size()) { + return Fail(nullptr, report, "channel allocation count does not match lower-layer route count"); + } + if (remoteCcuBuffers.size() > std::numeric_limits::max()) { + return Fail(nullptr, report, "too many lower-layer CCU remote routes"); + } + + TileXRCcuLowerLayerTransportSnapshot result; + result.msidToken.dieId = basicInfo.dieId; + result.msidToken.msId = basicInfo.msId; + if (basicInfo.msidToken.valid && basicInfo.msidToken.tokenId != 0) { + result.msidToken.tokenId = basicInfo.msidToken.tokenId; + result.msidToken.tokenValue = basicInfo.msidToken.tokenValue; + result.msidToken.valid = true; + } + result.dieId = basicInfo.dieId; + result.pfeId = allocation.channels.startId; + result.pfeOffset = SelectLowerLayerPfeOffset(basicInfo.dieId, result.pfeId); + result.startJettyId = TILEXR_CCU_DEFAULT_START_JETTY_ID; + result.startLocalJettyCtxId = TILEXR_CCU_DEFAULT_START_LOCAL_JETTY_CTX_ID; + ApplyLowerLayerPfePartition(result.pfeId, &result); + result.xnStartId = allocation.localXn.startId; + result.xnCount = allocation.localXn.num; + result.ckeStartId = localWaitCke.startId; + result.ckeCount = localWaitCke.num; + result.routes.reserve(remoteCcuBuffers.size()); + + const uint16_t wqeBasicBlockStride = SelectLowerLayerWqeBasicBlockStride(); + for (uint32_t i = 0; i < remoteCcuBuffers.size(); ++i) { + const auto& remoteCcuBuffer = remoteCcuBuffers[i]; + if (remoteCcuBuffer.remoteCcuVa == 0) { + return Fail(nullptr, report, "missing remote CCU VA for lower-layer route"); + } + TileXRCcuLowerLayerTransportRoute route; + route.channelId = allocation.channels.startId + i; + route.peerRank = remoteCcuBuffer.peerRank == TILEXR_CCU_REMOTE_PEER_RANK_UNKNOWN ? + i : + remoteCcuBuffer.peerRank; + route.remoteXnId = remoteCcuBuffer.remoteXnId == 0 ? + static_cast(allocation.remoteXn.startId + i) : + remoteCcuBuffer.remoteXnId; + route.remoteNotifyCke = remoteCcuBuffer.remoteNotifyCke == 0 ? + static_cast(remoteNotifyCke.startId + i) : + remoteCcuBuffer.remoteNotifyCke; + if (i > std::numeric_limits::max() / wqeBasicBlockStride) { + return Fail(nullptr, report, "lower-layer CCU WQE basic block start overflows"); + } + route.wqeBasicBlockStartId = static_cast(i * wqeBasicBlockStride); + route.remoteCcuVa = remoteCcuBuffer.remoteCcuVa; + route.memoryTokenId = remoteCcuBuffer.memoryTokenId; + route.memoryTokenValue = remoteCcuBuffer.memoryTokenValue; + route.channelResourceOwnerVerified = remoteCcuBuffer.channelResourceOwnerVerified; + route.transportResourceExchangeVerified = remoteCcuBuffer.transportResourceExchangeVerified; + if (remoteCcuBuffer.endpointRouteVerified && + !IsEmptyEndpointEid(remoteCcuBuffer.remoteEid) && + remoteCcuBuffer.doorbellVa != 0 && + remoteCcuBuffer.doorbellTokenId != 0 && + remoteCcuBuffer.sqDepth != 0) { + route.remoteEid = remoteCcuBuffer.remoteEid; + route.tpn = remoteCcuBuffer.tpn; + route.doorbellVa = remoteCcuBuffer.doorbellVa; + route.doorbellTokenId = remoteCcuBuffer.doorbellTokenId; + route.doorbellTokenValue = remoteCcuBuffer.doorbellTokenValue; + route.sqDepth = remoteCcuBuffer.sqDepth; + route.localDoorbellVa = remoteCcuBuffer.localDoorbellVa; + route.localDoorbellTokenId = remoteCcuBuffer.localDoorbellTokenId; + route.localDoorbellTokenValue = remoteCcuBuffer.localDoorbellTokenValue; + route.localSqDepth = remoteCcuBuffer.localSqDepth; + route.endpointRouteVerified = true; + } + result.routes.push_back(route); + } + + *snapshot = result; + FillTemplateReport(*snapshot, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuOverlayVerifiedEndpointRoutes( + const std::vector& verifiedRoutes, + TileXRCcuLowerLayerTransportSnapshot* snapshot, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + ResetReport(report); + if (snapshot == nullptr) { + return Fail(nullptr, report, "missing lower-layer CCU transport snapshot for endpoint route overlay"); + } + if (verifiedRoutes.empty()) { + FillTemplateReport(*snapshot, report); + return TILEXR_SUCCESS; + } + + for (const auto& verified : verifiedRoutes) { + if (!HasCompleteVerifiedEndpointRoute(verified)) { + return Fail(nullptr, report, "verified endpoint route is incomplete"); + } + auto routeIt = std::find_if( + snapshot->routes.begin(), + snapshot->routes.end(), + [&verified](const TileXRCcuLowerLayerTransportRoute& route) { + return route.channelId == verified.channelId; + }); + if (routeIt == snapshot->routes.end()) { + return Fail(nullptr, report, "verified endpoint route does not match an allocated channel"); + } + + routeIt->remoteEid = verified.remoteEid; + routeIt->tpn = verified.tpn; + routeIt->doorbellVa = verified.doorbellVa; + routeIt->doorbellTokenId = verified.doorbellTokenId; + routeIt->doorbellTokenValue = verified.doorbellTokenValue; + routeIt->sqDepth = verified.sqDepth; + routeIt->localDoorbellVa = verified.localDoorbellVa; + routeIt->localDoorbellTokenId = verified.localDoorbellTokenId; + routeIt->localDoorbellTokenValue = verified.localDoorbellTokenValue; + routeIt->localSqDepth = verified.localSqDepth; + routeIt->endpointRouteVerified = true; + routeIt->channelResourceOwnerVerified = verified.channelResourceOwnerVerified; + routeIt->transportResourceExchangeVerified = verified.transportResourceExchangeVerified; + } + + FillTemplateReport(*snapshot, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot& snapshot, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report) +{ + ResetReport(report); + if (plan == nullptr) { + return Fail(nullptr, report, "missing output lower-layer CCU install plan"); + } + *plan = TileXRCcuLowerLayerInstallPlan{}; + + if (snapshot.routes.empty()) { + return Fail(plan, report, "missing lower-layer CCU transport routes"); + } + if (snapshot.routes.size() > std::numeric_limits::max()) { + return Fail(plan, report, "too many lower-layer CCU transport routes"); + } + + TileXRCcuLowerLayerPlanSpec spec; + spec.msidToken = snapshot.msidToken; + spec.pfe.dieId = snapshot.dieId; + spec.pfe.pfeOffset = snapshot.pfeOffset; + spec.pfe.startJettyId = snapshot.startJettyId; + spec.pfe.jettyCount = snapshot.pfeJettyCount; + spec.pfe.startLocalJettyCtxId = snapshot.startLocalJettyCtxId; + spec.xnClear.dieId = snapshot.dieId; + spec.xnClear.startXnId = snapshot.xnStartId; + spec.xnClear.count = snapshot.xnCount; + spec.xnClear.valid = snapshot.xnCount != 0; + spec.ckeClear.dieId = snapshot.dieId; + spec.ckeClear.startCkeId = snapshot.ckeStartId; + spec.ckeClear.count = snapshot.ckeCount; + spec.ckeClear.valid = snapshot.ckeCount != 0; + + uint32_t routeIndex = 0; + for (const auto& route : snapshot.routes) { + TileXRCcuLowerLayerJettySpec jetty; + jetty.dieId = snapshot.dieId; + jetty.pfeId = snapshot.pfeId; + jetty.startJettyCtxId = static_cast(snapshot.startLocalJettyCtxId + routeIndex); + jetty.doorbellVa = route.localDoorbellVa == 0 ? route.doorbellVa : route.localDoorbellVa; + jetty.doorbellTokenId = route.localDoorbellTokenId == 0 ? + route.doorbellTokenId : + route.localDoorbellTokenId; + jetty.doorbellTokenValue = route.localDoorbellVa == 0 ? + route.doorbellTokenValue : + route.localDoorbellTokenValue; + jetty.sqDepth = route.localSqDepth == 0 ? route.sqDepth : route.localSqDepth; + jetty.wqeBasicBlockStartId = route.wqeBasicBlockStartId; + spec.jettys.push_back(jetty); + + TileXRCcuLowerLayerChannelSpec channel; + channel.dieId = snapshot.dieId; + channel.channelId = route.channelId; + channel.remoteEid = route.remoteEid; + channel.tpn = route.tpn; + channel.sourcePfeId = snapshot.pfeId; + channel.startJettyId = route.startJettyId == 0 ? + static_cast(snapshot.startJettyId + routeIndex) : + route.startJettyId; + channel.jettyCount = 1; + channel.memoryTokenId = route.memoryTokenId; + channel.memoryTokenValue = route.memoryTokenValue; + channel.remoteCcuVa = route.remoteCcuVa; + const auto channelIt = std::find_if( + spec.channels.begin(), + spec.channels.end(), + [&channel](const TileXRCcuLowerLayerChannelSpec& existing) { + return existing.dieId == channel.dieId && existing.channelId == channel.channelId; + }); + if (channelIt == spec.channels.end()) { + spec.channels.push_back(channel); + } + + TileXRCcuRemoteXnBindingProof remoteXn; + remoteXn.dieId = snapshot.dieId; + remoteXn.channelId = static_cast(route.channelId); + remoteXn.localXn = static_cast(snapshot.xnStartId + routeIndex); + remoteXn.remoteXn = route.remoteXnId; + remoteXn.notifyCke = route.remoteNotifyCke == 0 ? + static_cast(snapshot.ckeStartId + routeIndex) : + route.remoteNotifyCke; + remoteXn.peerRank = route.peerRank; + remoteXn.peerExchangeObserved = route.remoteXnId != 0; + remoteXn.localWaitCke = static_cast(snapshot.ckeStartId + routeIndex); + remoteXn.endpointRouteVerified = route.endpointRouteVerified; + remoteXn.channelResourceOwnerVerified = route.channelResourceOwnerVerified; + remoteXn.transportResourceExchangeVerified = route.transportResourceExchangeVerified; + spec.remoteXnBindings.push_back(remoteXn); + ++routeIndex; + } + + const int ret = TileXRCcuBuildLowerLayerInstallPlan(spec, plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + AppendRemoteXnClears(snapshot, plan); + AppendRemoteNotifyCkeClears(snapshot, plan); + FillReport(*plan, report); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.h b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.h new file mode 100644 index 0000000..26ff8c7 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_LOWER_LAYER_PLAN_BUILDER_H +#define TILEXR_CCU_LOWER_LAYER_PLAN_BUILDER_H + +#include "ccu/tilexr_ccu_install_provider.h" +#include "ccu/tilexr_ccu_lower_layer_payloads.h" + +#include +#include +#include +#include + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_REMOTE_PEER_RANK_UNKNOWN = 0xffffffffU; + +struct TileXRCcuLowerLayerMsidTokenSpec { + uint8_t dieId = 0; + uint32_t msId = 0; + uint32_t tokenId = 0; + uint32_t tokenValue = 0; + bool valid = false; +}; + +struct TileXRCcuLowerLayerPfeSpec { + uint8_t dieId = 0; + uint32_t pfeOffset = 0; + uint16_t startJettyId = 0; + uint16_t jettyCount = 0; + uint16_t startLocalJettyCtxId = 0; +}; + +struct TileXRCcuLowerLayerJettySpec { + uint8_t dieId = 0; + uint32_t pfeId = 0; + uint16_t startJettyCtxId = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + uint16_t wqeBasicBlockStartId = 0; +}; + +struct TileXRCcuLowerLayerChannelSpec { + uint8_t dieId = 0; + uint32_t channelId = 0; + std::array remoteEid {}; + uint32_t tpn = 0; + uint32_t sourcePfeId = 0; + uint16_t startJettyId = 0; + uint16_t jettyCount = 0; + uint32_t memoryTokenId = 0; + uint32_t memoryTokenValue = 0; + uint64_t remoteCcuVa = 0; +}; + +struct TileXRCcuLowerLayerCkeClearSpec { + uint8_t dieId = 0; + uint32_t startCkeId = 0; + uint32_t count = 0; + bool valid = false; +}; + +struct TileXRCcuLowerLayerXnClearSpec { + uint8_t dieId = 0; + uint32_t startXnId = 0; + uint32_t count = 0; + bool valid = false; +}; + +struct TileXRCcuLowerLayerPlanSpec { + TileXRCcuLowerLayerMsidTokenSpec msidToken; + TileXRCcuLowerLayerPfeSpec pfe; + std::vector jettys; + std::vector channels; + TileXRCcuLowerLayerXnClearSpec xnClear; + TileXRCcuLowerLayerCkeClearSpec ckeClear; + std::vector remoteXnBindings; +}; + +struct TileXRCcuLowerLayerPlanBuilderReport { + uint32_t msidTokenCount = 0; + uint32_t pfeCount = 0; + uint32_t jettyCount = 0; + uint32_t localJettyCtxCount = 0; + uint32_t channelCount = 0; + uint32_t ckeClearCount = 0; + std::string message; +}; + +struct TileXRCcuRemoteCcuBufferInfo { + uint64_t remoteCcuVa = 0; + uint32_t peerRank = TILEXR_CCU_REMOTE_PEER_RANK_UNKNOWN; + uint32_t memoryTokenId = 0; + uint32_t rawMemoryTokenId = 0; + uint32_t memoryTokenValue = 0; + uint16_t remoteXnId = 0; + uint16_t remoteNotifyCke = 0; + std::array remoteEid {}; + uint32_t tpn = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + uint64_t localDoorbellVa = 0; + uint32_t localDoorbellTokenId = 0; + uint32_t localDoorbellTokenValue = 0; + uint32_t localSqDepth = 0; + bool endpointRouteVerified = false; + bool channelResourceOwnerVerified = false; + bool transportResourceExchangeVerified = false; +}; + +struct TileXRCcuLowerLayerTransportRoute { + uint32_t channelId = 0; + uint32_t peerRank = 0; + uint16_t remoteXnId = 0; + uint16_t remoteNotifyCke = 0; + std::array remoteEid {}; + uint32_t tpn = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + uint64_t localDoorbellVa = 0; + uint32_t localDoorbellTokenId = 0; + uint32_t localDoorbellTokenValue = 0; + uint32_t localSqDepth = 0; + uint16_t startJettyId = 0; + uint16_t wqeBasicBlockStartId = 0; + uint32_t memoryTokenId = 0; + uint32_t memoryTokenValue = 0; + uint64_t remoteCcuVa = 0; + bool endpointRouteVerified = false; + bool channelResourceOwnerVerified = false; + bool transportResourceExchangeVerified = false; +}; + +struct TileXRCcuLowerLayerTransportSnapshot { + TileXRCcuLowerLayerMsidTokenSpec msidToken; + uint8_t dieId = 0; + uint32_t pfeOffset = 0; + uint32_t pfeId = 0; + uint16_t startJettyId = 0; + uint16_t pfeJettyCount = 0; + uint16_t startLocalJettyCtxId = 0; + uint32_t xnStartId = 0; + uint32_t xnCount = 0; + uint32_t ckeStartId = 0; + uint32_t ckeCount = 0; + std::vector routes; +}; + +int TileXRCcuBuildLowerLayerInstallPlan( + const TileXRCcuLowerLayerPlanSpec& spec, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report); + +int TileXRCcuBuildLowerLayerTransportTemplate( + const TileXRCcuBasicInfo& basicInfo, + const TileXRCcuResourceAllocation& allocation, + const std::vector& remoteCcuBuffers, + TileXRCcuLowerLayerTransportSnapshot* snapshot, + TileXRCcuLowerLayerPlanBuilderReport* report); + +int TileXRCcuOverlayVerifiedEndpointRoutes( + const std::vector& verifiedRoutes, + TileXRCcuLowerLayerTransportSnapshot* snapshot, + TileXRCcuLowerLayerPlanBuilderReport* report); + +int TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot& snapshot, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_LOWER_LAYER_PLAN_BUILDER_H diff --git a/src/comm/ccu/tilexr_ccu_memory_program.cpp b/src/comm/ccu/tilexr_ccu_memory_program.cpp new file mode 100644 index 0000000..a1559ab --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_memory_program.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_memory_program.h" + +namespace TileXR { +namespace { + +constexpr uint64_t TILEXR_CCU_TOKEN_VALID_SHIFT = 52ULL; +constexpr uint64_t TILEXR_CCU_TOKEN_ID_SHIFT = 32ULL; +constexpr uint64_t TILEXR_CCU_TOKEN_ID_MASK = 0xfffffULL; +constexpr uint64_t TILEXR_CCU_TOKEN_VALUE_MASK = 0xffffffffULL; + +void ResetReport(TileXRCcuMemoryProgramReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuMemoryProgramReport{}; + } +} + +int Fail( + std::vector* program, + TileXRCcuMemoryProgramReport* report, + const std::string& message) +{ + if (program != nullptr) { + program->clear(); + } + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +int ValidateSpec( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report) +{ + if (program == nullptr) { + return Fail(program, report, "missing output CCU memory copy program"); + } + if (spec.localGsa == 0 || spec.localXn == 0 || spec.remoteGsa == 0 || spec.remoteXn == 0 || + spec.lengthXn == 0) { + return Fail(program, report, "missing CCU memory copy GSA/XN resources"); + } + if (spec.localAddr == 0 || spec.localToken == 0 || spec.remoteAddr == 0 || spec.remoteToken == 0) { + return Fail(program, report, "missing CCU memory copy address/token inputs"); + } + if (spec.lengthBytes == 0) { + return Fail(program, report, "missing CCU memory copy length"); + } + if (spec.channelId == 0) { + return Fail(program, report, "missing CCU memory copy channel"); + } + if (spec.completionCke == 0 || spec.completionMask == 0) { + return Fail(program, report, "missing CCU memory copy completion CKE"); + } + if (spec.reduceDataType > 0xfU || spec.reduceOpCode > 0xfU) { + return Fail(program, report, "CCU memory copy reduce fields exceed v1 encoding width"); + } + return TILEXR_SUCCESS; +} + +int AppendLoadImmediates( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToGsa(spec.localGsa, spec.localAddr, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode local memory address GSA load"); + } + program->push_back(instr); + + if (TileXRCcuEncodeLoadImdToXn(spec.localXn, spec.localToken, 1U, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode local memory token XN load"); + } + program->push_back(instr); + + if (TileXRCcuEncodeLoadImdToGsa(spec.remoteGsa, spec.remoteAddr, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode remote memory address GSA load"); + } + program->push_back(instr); + + if (TileXRCcuEncodeLoadImdToXn(spec.remoteXn, spec.remoteToken, 1U, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode remote memory token XN load"); + } + program->push_back(instr); + + if (TileXRCcuEncodeLoadImdToXn(spec.lengthXn, spec.lengthBytes, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode memory copy length XN load"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendTransfer( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report) +{ + TileXRCcuMemTransferSpec transfer; + transfer.localGsa = spec.localGsa; + transfer.localXn = spec.localXn; + transfer.remoteGsa = spec.remoteGsa; + transfer.remoteXn = spec.remoteXn; + transfer.lengthXn = spec.lengthXn; + transfer.channelId = spec.channelId; + transfer.reduceDataType = spec.reduceDataType; + transfer.reduceOpCode = spec.reduceOpCode; + transfer.setCkeId = spec.completionCke; + transfer.setCkeMask = spec.completionMask; + transfer.clearWait = true; + transfer.lengthFromXn = true; + transfer.reduceEnabled = spec.reduceEnabled; + + TileXRCcuInstr instr; + const int ret = spec.direction == TileXRCcuMemoryCopyDirection::RemoteToLocal ? + TileXRCcuEncodeTransRmtMemToLocMem(transfer, &instr) : + TileXRCcuEncodeTransLocMemToRmtMem(transfer, &instr); + if (ret != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU memory transfer instruction"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendCompletionWait( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report) +{ + TileXRCcuCkeSpec wait; + wait.waitCkeId = spec.completionCke; + wait.waitMask = spec.completionMask; + wait.clearWait = true; + + TileXRCcuInstr instr; + if (TileXRCcuEncodeClearCke(wait, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode CCU memory copy completion wait"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +void FillReport(const std::vector& program, TileXRCcuMemoryProgramReport* report) +{ + if (report == nullptr) { + return; + } + report->loadInstructionCount = 5; + report->transferInstructionCount = 1; + report->waitInstructionCount = 1; + report->totalInstructionCount = static_cast(program.size()); + report->message = "ok"; +} + +} // namespace + +uint64_t TileXRCcuPackMemoryToken(uint32_t tokenId, uint32_t tokenValue, bool valid) +{ + const uint64_t validBits = valid ? 1ULL : 0ULL; + return (validBits << TILEXR_CCU_TOKEN_VALID_SHIFT) | + ((static_cast(tokenId) & TILEXR_CCU_TOKEN_ID_MASK) << TILEXR_CCU_TOKEN_ID_SHIFT) | + (static_cast(tokenValue) & TILEXR_CCU_TOKEN_VALUE_MASK); +} + +int TileXRCcuBuildMemoryCopyProgram( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report) +{ + ResetReport(report); + if (program != nullptr) { + program->clear(); + } + int ret = ValidateSpec(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + program->reserve(7); + ret = AppendLoadImmediates(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = AppendTransfer(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = AppendCompletionWait(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + FillReport(*program, report); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_memory_program.h b/src/comm/ccu/tilexr_ccu_memory_program.h new file mode 100644 index 0000000..19640b5 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_memory_program.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_MEMORY_PROGRAM_H +#define TILEXR_CCU_MEMORY_PROGRAM_H + +#include "ccu/tilexr_ccu_microcode.h" + +#include +#include +#include + +namespace TileXR { + +enum class TileXRCcuMemoryCopyDirection { + RemoteToLocal = 0, + LocalToRemote = 1, +}; + +struct TileXRCcuMemoryCopySpec { + TileXRCcuMemoryCopyDirection direction = TileXRCcuMemoryCopyDirection::RemoteToLocal; + uint16_t localGsa = 0; + uint16_t localXn = 0; + uint16_t remoteGsa = 0; + uint16_t remoteXn = 0; + uint16_t lengthXn = 0; + uint64_t localAddr = 0; + uint64_t localToken = 0; + uint64_t remoteAddr = 0; + uint64_t remoteToken = 0; + uint64_t lengthBytes = 0; + uint16_t channelId = 0; + uint16_t completionCke = 0; + uint16_t completionMask = 0; + uint16_t reduceDataType = 0; + uint16_t reduceOpCode = 0; + bool reduceEnabled = false; +}; + +struct TileXRCcuMemoryProgramReport { + uint32_t loadInstructionCount = 0; + uint32_t transferInstructionCount = 0; + uint32_t waitInstructionCount = 0; + uint32_t totalInstructionCount = 0; + std::string message; +}; + +uint64_t TileXRCcuPackMemoryToken(uint32_t tokenId, uint32_t tokenValue, bool valid); + +int TileXRCcuBuildMemoryCopyProgram( + const TileXRCcuMemoryCopySpec& spec, + std::vector* program, + TileXRCcuMemoryProgramReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_MEMORY_PROGRAM_H diff --git a/src/comm/ccu/tilexr_ccu_microcode.cpp b/src/comm/ccu/tilexr_ccu_microcode.cpp new file mode 100644 index 0000000..44abfdb --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_microcode.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_microcode.h" + +namespace TileXR { +namespace { + +constexpr uint64_t TILEXR_CCU_LOAD_SQE_ARGS_TO_X_HEADER = 0x0001U; +constexpr uint64_t TILEXR_CCU_LOAD_IMD_TO_GSA_HEADER = 0x0002U; +constexpr uint64_t TILEXR_CCU_LOAD_IMD_TO_XN_HEADER = 0x0003U; +constexpr uint64_t TILEXR_CCU_SET_CKE_HEADER = 0x0802U; +constexpr uint64_t TILEXR_CCU_CLEAR_CKE_HEADER = 0x0804U; +constexpr uint64_t TILEXR_CCU_TRANS_RMT_MEM_TO_LOC_MEM_HEADER = 0x1008U; +constexpr uint64_t TILEXR_CCU_TRANS_LOC_MEM_TO_RMT_MEM_HEADER = 0x1009U; +constexpr uint64_t TILEXR_CCU_SYNC_CKE_HEADER = 0x100bU; +constexpr uint64_t TILEXR_CCU_SYNC_XN_HEADER = 0x100dU; +constexpr uint64_t TILEXR_CCU_SYNC_XN_TRACE_FLAG = 0x0001000000000000ULL; + +void ClearInstr(TileXRCcuInstr* instr) +{ + for (auto& word : instr->words) { + word = 0; + } +} + +int ValidateInstrOutput(TileXRCcuInstr* instr) +{ + if (instr == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + ClearInstr(instr); + return TILEXR_SUCCESS; +} + +uint64_t PackSlots(uint16_t slot0, uint16_t slot1, uint16_t slot2, uint16_t slot3) +{ + return static_cast(slot0) | + (static_cast(slot1) << 16U) | + (static_cast(slot2) << 32U) | + (static_cast(slot3) << 48U); +} + +uint16_t ClearTypeBit(bool clearWait) +{ + return clearWait ? 1U : 0U; +} + +uint16_t TransferControlSlot(const TileXRCcuMemTransferSpec& spec) +{ + constexpr uint16_t udfType = 0; + return static_cast(udfType | + (static_cast(spec.reduceDataType) << 8U) | + (static_cast(spec.reduceOpCode) << 12U)); +} + +uint16_t TransferFlagSlot(const TileXRCcuMemTransferSpec& spec) +{ + return static_cast( + (spec.clearWait ? 1U : 0U) | + (spec.lengthFromXn ? 2U : 0U) | + (spec.reduceEnabled ? 4U : 0U)); +} + +int ValidateTransferSpec(const TileXRCcuMemTransferSpec& spec) +{ + if (spec.localGsa == 0 || spec.localXn == 0 || spec.remoteGsa == 0 || spec.remoteXn == 0 || + spec.lengthXn == 0 || spec.channelId == 0 || spec.reduceDataType > 0xfU || spec.reduceOpCode > 0xfU) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if ((spec.setCkeId == 0) != (spec.setCkeMask == 0) || + (spec.waitCkeId == 0) != (spec.waitCkeMask == 0)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +void WriteLe16(uint8_t* bytes, size_t offset, uint16_t value) +{ + bytes[offset] = static_cast(value & 0xffU); + bytes[offset + 1U] = static_cast((value >> 8U) & 0xffU); +} + +void WriteLe64(uint8_t* bytes, size_t offset, uint64_t value) +{ + for (size_t i = 0; i < sizeof(uint64_t); ++i) { + bytes[offset + i] = static_cast((value >> (i * 8U)) & 0xffU); + } +} + +uint64_t ReadLe64(const uint8_t* bytes, size_t offset) +{ + uint64_t value = 0; + for (size_t i = 0; i < sizeof(uint64_t); ++i) { + value |= static_cast(bytes[offset + i]) << (i * 8U); + } + return value; +} + +} // namespace + +int TileXRCcuEncodeLoadSqeArgsToX(uint16_t xnId, uint32_t sqeArgId, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (xnId == 0 || sqeArgId >= TILEXR_CCU_SQE_ARGS_LEN) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots(static_cast(TILEXR_CCU_LOAD_SQE_ARGS_TO_X_HEADER), xnId, + static_cast(sqeArgId), 0); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeLoadImdToXn(uint16_t xnId, uint64_t immediate, uint16_t secFlag, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (xnId == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + uint8_t bytes[sizeof(TileXRCcuInstr)] = {}; + WriteLe16(bytes, 0, static_cast(TILEXR_CCU_LOAD_IMD_TO_XN_HEADER)); + WriteLe16(bytes, 2, xnId); + WriteLe64(bytes, 4, immediate); + WriteLe16(bytes, 12, secFlag); + for (size_t i = 0; i < 4U; ++i) { + instr->words[i] = ReadLe64(bytes, i * sizeof(uint64_t)); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeLoadImdToGsa(uint16_t gsaId, uint64_t immediate, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (gsaId == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + uint8_t bytes[sizeof(TileXRCcuInstr)] = {}; + WriteLe16(bytes, 0, static_cast(TILEXR_CCU_LOAD_IMD_TO_GSA_HEADER)); + WriteLe16(bytes, 2, gsaId); + WriteLe64(bytes, 4, immediate); + for (size_t i = 0; i < 4U; ++i) { + instr->words[i] = ReadLe64(bytes, i * sizeof(uint64_t)); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeSyncXn(const TileXRCcuSyncXnSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (spec.remoteXn == 0 || spec.localXn == 0 || spec.channelId == 0 || spec.notifyCke == 0 || + spec.notifyMask == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots(static_cast(TILEXR_CCU_SYNC_XN_HEADER), spec.remoteXn, spec.localXn, 0); + instr->words[1] = PackSlots(spec.channelId, spec.notifyCke, spec.notifyMask, 0); + instr->words[2] = spec.clearWait ? TILEXR_CCU_SYNC_XN_TRACE_FLAG : 0; + instr->words[3] = PackSlots(spec.setCkeId, spec.setCkeMask, spec.waitCkeId, spec.waitCkeMask); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeSyncCke(const TileXRCcuSyncCkeSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (spec.remoteCke == 0 || spec.localCke == 0 || spec.localCkeMask == 0 || spec.channelId == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots( + static_cast(TILEXR_CCU_SYNC_CKE_HEADER), + spec.remoteCke, + spec.localCke, + spec.localCkeMask); + instr->words[1] = PackSlots(spec.channelId, 0, 0, 0); + instr->words[2] = PackSlots(0, 0, 0, ClearTypeBit(spec.clearWait)); + instr->words[3] = PackSlots(spec.setCkeId, spec.setCkeMask, spec.waitCkeId, spec.waitCkeMask); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeSetCke(const TileXRCcuCkeSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if ((spec.ckeId == 0 || spec.mask == 0) && (spec.waitCkeId == 0 || spec.waitMask == 0)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots(static_cast(TILEXR_CCU_SET_CKE_HEADER), ClearTypeBit(spec.clearWait), + spec.ckeId, spec.mask); + instr->words[1] = PackSlots(spec.waitCkeId, spec.waitMask, 0, 0); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeClearCke(const TileXRCcuCkeSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if ((spec.ckeId == 0 || spec.mask == 0) && (spec.waitCkeId == 0 || spec.waitMask == 0)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots(static_cast(TILEXR_CCU_CLEAR_CKE_HEADER), ClearTypeBit(spec.clearWait), + spec.ckeId, spec.mask); + instr->words[1] = PackSlots(spec.waitCkeId, spec.waitMask, 0, 0); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeTransRmtMemToLocMem(const TileXRCcuMemTransferSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS || ValidateTransferSpec(spec) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots( + static_cast(TILEXR_CCU_TRANS_RMT_MEM_TO_LOC_MEM_HEADER), + spec.localGsa, + spec.localXn, + spec.remoteGsa); + instr->words[1] = PackSlots(spec.remoteXn, spec.lengthXn, spec.channelId, TransferControlSlot(spec)); + instr->words[2] = PackSlots(0, 0, 0, TransferFlagSlot(spec)); + instr->words[3] = PackSlots(spec.setCkeId, spec.setCkeMask, spec.waitCkeId, spec.waitCkeMask); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeTransLocMemToRmtMem(const TileXRCcuMemTransferSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS || ValidateTransferSpec(spec) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots( + static_cast(TILEXR_CCU_TRANS_LOC_MEM_TO_RMT_MEM_HEADER), + spec.remoteGsa, + spec.remoteXn, + spec.localGsa); + instr->words[1] = PackSlots(spec.localXn, spec.lengthXn, spec.channelId, TransferControlSlot(spec)); + instr->words[2] = PackSlots(0, 0, 0, TransferFlagSlot(spec)); + instr->words[3] = PackSlots(spec.setCkeId, spec.setCkeMask, spec.waitCkeId, spec.waitCkeMask); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildSqeLoadProgram(uint16_t firstXnId, uint32_t argCount, std::vector* program) +{ + if (program == nullptr || firstXnId == 0 || argCount == 0 || argCount > TILEXR_CCU_SQE_ARGS_LEN) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + program->clear(); + program->reserve(argCount); + for (uint32_t argId = 0; argId < argCount; ++argId) { + TileXRCcuInstr instr; + const uint32_t xnId = static_cast(firstXnId) + argId; + if (xnId > UINT16_MAX || TileXRCcuEncodeLoadSqeArgsToX(static_cast(xnId), argId, &instr) != + TILEXR_SUCCESS) { + program->clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + program->push_back(instr); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildSyncProgram(const std::vector& specs, std::vector* program) +{ + if (program == nullptr || specs.empty()) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + program->clear(); + program->reserve(specs.size()); + for (const auto& spec : specs) { + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncXn(spec, &instr) != TILEXR_SUCCESS) { + program->clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + program->push_back(instr); + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_microcode.h b/src/comm/ccu/tilexr_ccu_microcode.h new file mode 100644 index 0000000..fe8c032 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_microcode.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_MICROCODE_H +#define TILEXR_CCU_MICROCODE_H + +#include "ccu/tilexr_ccu_runtime.h" + +#include +#include + +namespace TileXR { + +struct TileXRCcuInstr { + uint64_t words[4] = {}; +}; + +struct TileXRCcuSyncXnSpec { + uint16_t remoteXn = 0; + uint16_t localXn = 0; + uint16_t channelId = 0; + uint16_t notifyCke = 0; + uint16_t notifyMask = 0; + uint16_t setCkeId = 0; + uint16_t setCkeMask = 0; + uint16_t waitCkeId = 0; + uint16_t waitCkeMask = 0; + bool clearWait = true; +}; + +struct TileXRCcuSyncCkeSpec { + uint16_t remoteCke = 0; + uint16_t localCke = 0; + uint16_t localCkeMask = 0; + uint16_t channelId = 0; + uint16_t setCkeId = 0; + uint16_t setCkeMask = 0; + uint16_t waitCkeId = 0; + uint16_t waitCkeMask = 0; + bool clearWait = true; +}; + +struct TileXRCcuCkeSpec { + uint16_t ckeId = 0; + uint16_t mask = 0; + uint16_t waitCkeId = 0; + uint16_t waitMask = 0; + bool clearWait = true; +}; + +struct TileXRCcuMemTransferSpec { + uint16_t localGsa = 0; + uint16_t localXn = 0; + uint16_t remoteGsa = 0; + uint16_t remoteXn = 0; + uint16_t lengthXn = 0; + uint16_t channelId = 0; + uint16_t reduceDataType = 0; + uint16_t reduceOpCode = 0; + uint16_t setCkeId = 0; + uint16_t setCkeMask = 0; + uint16_t waitCkeId = 0; + uint16_t waitCkeMask = 0; + bool clearWait = true; + bool lengthFromXn = true; + bool reduceEnabled = false; +}; + +int TileXRCcuEncodeLoadSqeArgsToX(uint16_t xnId, uint32_t sqeArgId, TileXRCcuInstr* instr); + +int TileXRCcuEncodeLoadImdToXn(uint16_t xnId, uint64_t immediate, uint16_t secFlag, TileXRCcuInstr* instr); + +int TileXRCcuEncodeLoadImdToGsa(uint16_t gsaId, uint64_t immediate, TileXRCcuInstr* instr); + +int TileXRCcuEncodeSyncXn(const TileXRCcuSyncXnSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeSyncCke(const TileXRCcuSyncCkeSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeSetCke(const TileXRCcuCkeSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeClearCke(const TileXRCcuCkeSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeTransRmtMemToLocMem(const TileXRCcuMemTransferSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeTransLocMemToRmtMem(const TileXRCcuMemTransferSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuBuildSqeLoadProgram( + uint16_t firstXnId, + uint32_t argCount, + std::vector* program); + +int TileXRCcuBuildSyncProgram( + const std::vector& specs, + std::vector* program); + +} // namespace TileXR + +#endif // TILEXR_CCU_MICROCODE_H diff --git a/src/comm/ccu/tilexr_ccu_producer_plan.cpp b/src/comm/ccu/tilexr_ccu_producer_plan.cpp new file mode 100644 index 0000000..d87cd50 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_producer_plan.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_producer_plan.h" + +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr uint32_t TILEXR_CCU_HCOMM_TASK1_PRELUDE_LOAD_ARG_COUNT = 2U; + +void ResetReport(TileXRCcuProducerPlanReport* report) +{ + if (report == nullptr) { + return; + } + report->syncResourceCount = 0; + report->taskCount = 0; + report->instructionCount = 0; + report->message.clear(); +} + +int Fail(TileXRCcuProducerPlanReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +bool ContainsId(const TileXRCcuRange& range, uint8_t dieId, uint16_t id) +{ + if (range.dieId != dieId || range.num == 0) { + return false; + } + const uint32_t begin = range.startId; + const uint32_t end = begin + range.num; + return id >= begin && id < end; +} + +bool InstructionWindowContains(const TileXRCcuInstructionWindow& window, uint8_t dieId, uint16_t start, uint16_t count) +{ + if (window.dieId != dieId || window.missionCount == 0 || count == 0) { + return false; + } + const uint32_t begin = window.missionStartId; + const uint32_t end = begin + window.missionCount; + const uint32_t taskBegin = start; + const uint32_t taskEnd = taskBegin + count; + return taskBegin >= begin && taskEnd <= end; +} + +uint16_t EffectiveLocalWaitCke(const TileXRCcuSyncResource& resource) +{ + return resource.localWaitCke == 0 ? resource.notifyCke : resource.localWaitCke; +} + +uint16_t EffectiveRemoteNotifyMask(const TileXRCcuSyncResource& resource) +{ + return resource.remoteNotifyMask == 0 ? 1U : resource.remoteNotifyMask; +} + +uint16_t EffectiveLocalWaitMask(const TileXRCcuSyncResource& resource) +{ + return resource.localWaitMask == 0 ? 1U : resource.localWaitMask; +} + +uint16_t EffectiveSourceCkeMask(const TileXRCcuSyncResource& resource) +{ + return resource.sourceCkeMask == 0 ? 0xffffU : resource.sourceCkeMask; +} + +bool SyncCkeMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncCke || + mode == TileXRCcuBarrierMode::SyncCkeSetWait || + mode == TileXRCcuBarrierMode::SyncCkePostOnly; +} + +bool SyncXnMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXn || + mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +bool RequiresHcommStyleTask1Prelude(const TileXRCcuProducerPlan& plan) +{ + return plan.taskWindows.size() > 1 && SyncXnMode(plan.barrierMode); +} + +uint32_t SqeLoadXnOffset(uint32_t argId) +{ + return argId; +} + +uint32_t HcommStyleTask1PreludeLoadXnOffset(uint32_t argId) +{ + return argId; +} + +uint16_t HcommStylePreludeReserveXn(const TileXRCcuProducerPlan& plan) +{ + return static_cast( + static_cast(plan.kernelLocalXn.startId) + TILEXR_CCU_SQE_ARGS_LEN); +} + +bool HasKernelLocalGsa(const TileXRCcuProducerPlan& plan) +{ + return plan.kernelLocalGsa.dieId == plan.mission.dieId && plan.kernelLocalGsa.startId != 0 && + plan.kernelLocalGsa.num != 0; +} + +int AppendSqeLoadProgram( + const TileXRCcuProducerPlan& plan, + uint32_t argCount, + std::vector* program, + TileXRCcuProducerPlanReport* report) +{ + if (program == nullptr || plan.kernelLocalXn.startId == 0 || argCount == 0 || argCount > TILEXR_CCU_SQE_ARGS_LEN) { + return Fail(report, "invalid SQE argument load microcode request"); + } + + program->clear(); + program->reserve(argCount); + for (uint32_t argId = 0; argId < argCount; ++argId) { + const uint32_t xnId = static_cast(plan.kernelLocalXn.startId) + SqeLoadXnOffset(argId); + if (xnId > std::numeric_limits::max() || + !ContainsId(plan.kernelLocalXn, plan.mission.dieId, static_cast(xnId))) { + program->clear(); + return Fail(report, "SQE argument load XN is outside the kernel-local XN repository range"); + } + + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadSqeArgsToX(static_cast(xnId), argId, &instr) != TILEXR_SUCCESS) { + program->clear(); + return Fail(report, "failed to encode SQE argument load microcode"); + } + program->push_back(instr); + } + return TILEXR_SUCCESS; +} + +int ValidateMission(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (!plan.mission.installed || plan.mission.key == 0) { + return Fail(report, "missing installed mission/key for CCU producer plan"); + } + if (!ContainsId(plan.kernelLocalMission, plan.mission.dieId, plan.mission.missionId)) { + return Fail(report, "mission id is outside the kernel-local mission repository range"); + } + return TILEXR_SUCCESS; +} + +int ValidateInstructionWindow(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (plan.instructionWindow.repositoryCount == 0 || plan.instructionWindow.missionCount == 0) { + return Fail(report, "missing instruction repository window for CCU producer plan"); + } + if (plan.instructionWindow.dieId != plan.mission.dieId) { + return Fail(report, "instruction repository die does not match mission die"); + } + const uint32_t repositoryEnd = plan.instructionWindow.repositoryStartId + plan.instructionWindow.repositoryCount; + const uint32_t missionEnd = plan.instructionWindow.missionStartId + plan.instructionWindow.missionCount; + if (plan.instructionWindow.missionStartId < plan.instructionWindow.repositoryStartId || missionEnd > repositoryEnd) { + return Fail(report, "mission instruction window is outside the instruction repository range"); + } + return TILEXR_SUCCESS; +} + +int ValidateKernelLocalRepositories(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (plan.kernelLocalXn.dieId != plan.mission.dieId || plan.kernelLocalXn.num == 0) { + return Fail(report, "missing kernel-local XN repository range"); + } + if (plan.kernelLocalCke.dieId != plan.mission.dieId || plan.kernelLocalCke.num == 0) { + return Fail(report, "missing kernel-local CKE repository range"); + } + return TILEXR_SUCCESS; +} + +int ValidateSyncResources(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (plan.syncResources.empty()) { + return Fail(report, "missing CCU sync resources"); + } + std::set channelIds; + for (const auto& resource : plan.syncResources) { + if (resource.dieId != plan.mission.dieId) { + return Fail(report, "sync resource die does not match mission die"); + } + if (resource.channelId == 0) { + return Fail(report, "missing channel id for sync resource"); + } + if (!channelIds.insert(resource.channelId).second) { + return Fail(report, "duplicate channel id for sync resource"); + } + if (!ContainsId(plan.kernelLocalXn, resource.dieId, resource.localXn)) { + return Fail(report, "local XN is outside the kernel-local XN repository range"); + } + if (resource.remoteXn == 0) { + return Fail(report, "missing channel-bound remote XN"); + } + if (resource.notifyCke == 0) { + return Fail(report, "missing remote notify CKE resource"); + } + if (EffectiveRemoteNotifyMask(resource) == 0) { + return Fail(report, "missing remote notify CKE mask"); + } + const uint16_t localWaitCke = EffectiveLocalWaitCke(resource); + if (localWaitCke == 0) { + return Fail(report, "missing local wait CKE resource"); + } + if (EffectiveLocalWaitMask(resource) == 0) { + return Fail(report, "missing local wait CKE mask"); + } + if (resource.localWaitCke != 0 && !ContainsId(plan.kernelLocalCke, resource.dieId, localWaitCke)) { + return Fail(report, "local wait CKE is outside the kernel-local CKE repository range"); + } + if (SyncCkeMode(plan.barrierMode)) { + if (resource.sourceCke == 0) { + return Fail(report, "missing source CKE resource for SyncCKE barrier"); + } + if (EffectiveSourceCkeMask(resource) == 0) { + return Fail(report, "missing source CKE mask for SyncCKE barrier"); + } + if (!ContainsId(plan.kernelLocalCke, resource.dieId, resource.sourceCke)) { + return Fail(report, "source CKE is outside the kernel-local CKE repository range"); + } + } + if (resource.bindingCount == 0) { + return Fail(report, "missing channel variable binding for sync resource"); + } + } + return TILEXR_SUCCESS; +} + +int ValidateTasks(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (plan.taskWindows.empty()) { + return Fail(report, "missing CCU task windows"); + } + for (const auto& window : plan.taskWindows) { + if (window.dieId != plan.mission.dieId) { + return Fail(report, "task die does not match mission die"); + } + if (!InstructionWindowContains(plan.instructionWindow, window.dieId, window.instStartId, window.instCnt)) { + return Fail(report, "task instruction range is outside the loaded mission instruction window"); + } + if (window.argSize != 1 && window.argSize != TILEXR_CCU_SQE_ARGS_LEN) { + return Fail(report, "task argSize must match a supported CCU SQE payload shape"); + } + if (window.args.size() > TILEXR_CCU_SQE_ARGS_LEN) { + return Fail(report, "task args exceed the CCU SQE payload capacity"); + } + } + return TILEXR_SUCCESS; +} + +void FillReport(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + if (report == nullptr) { + return; + } + report->syncResourceCount = static_cast(plan.syncResources.size()); + report->taskCount = static_cast(plan.taskWindows.size()); + report->instructionCount = plan.instructionWindow.repositoryCount; + report->message = "ok"; +} + +int AppendHcommStyleTask1Prelude( + const TileXRCcuProducerPlan& plan, + std::vector* program, + TileXRCcuProducerPlanReport* report) +{ + if (!RequiresHcommStyleTask1Prelude(plan)) { + return TILEXR_SUCCESS; + } + + const uint16_t reserveXn = HcommStylePreludeReserveXn(plan); + if (!ContainsId(plan.kernelLocalXn, plan.mission.dieId, reserveXn)) { + return Fail(report, "missing reserve XN for hcomm-style task1 prelude"); + } + + for (uint32_t argId = 0; argId < TILEXR_CCU_HCOMM_TASK1_PRELUDE_LOAD_ARG_COUNT; ++argId) { + const uint32_t xnId = + static_cast(plan.kernelLocalXn.startId) + HcommStyleTask1PreludeLoadXnOffset(argId); + if (xnId > std::numeric_limits::max() || + !ContainsId(plan.kernelLocalXn, plan.mission.dieId, static_cast(xnId))) { + return Fail(report, "missing load-arg XN for hcomm-style task1 prelude"); + } + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadSqeArgsToX(static_cast(xnId), argId, &instr) != TILEXR_SUCCESS) { + return Fail(report, "failed to encode hcomm-style task1 load-arg prelude"); + } + program->push_back(instr); + } + + TileXRCcuInstr reserveXnInstr; + if (TileXRCcuEncodeLoadImdToXn(reserveXn, 0, 0, &reserveXnInstr) != TILEXR_SUCCESS) { + return Fail(report, "failed to encode hcomm-style task1 reserve XN prelude"); + } + program->push_back(reserveXnInstr); + + TileXRCcuInstr reserveAddrInstr; + if (HasKernelLocalGsa(plan)) { + if (TileXRCcuEncodeLoadImdToGsa(plan.kernelLocalGsa.startId, 0, &reserveAddrInstr) != TILEXR_SUCCESS) { + return Fail(report, "failed to encode hcomm-style task1 reserve GSA prelude"); + } + } else if (TileXRCcuEncodeLoadImdToXn(reserveXn, 0, 0, &reserveAddrInstr) != TILEXR_SUCCESS) { + return Fail(report, "failed to encode hcomm-style task1 nop prelude"); + } + program->push_back(reserveAddrInstr); + + const TileXRCcuSyncResource& firstResource = plan.syncResources.front(); + TileXRCcuCkeSpec notifyInit; + notifyInit.ckeId = firstResource.notifyCke; + notifyInit.mask = EffectiveRemoteNotifyMask(firstResource); + notifyInit.clearWait = false; + TileXRCcuInstr notifyInstr; + if (TileXRCcuEncodeSetCke(notifyInit, ¬ifyInstr) != TILEXR_SUCCESS) { + return Fail(report, "failed to encode hcomm-style task1 notify CKE prelude"); + } + program->push_back(notifyInstr); + return TILEXR_SUCCESS; +} + +} // namespace + +int TileXRCcuValidateProducerPlan(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report) +{ + ResetReport(report); + + int ret = ValidateMission(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = ValidateInstructionWindow(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = ValidateKernelLocalRepositories(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = ValidateSyncResources(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = ValidateTasks(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + FillReport(plan, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildTasks( + const TileXRCcuProducerPlan& plan, + std::vector* tasks, + TileXRCcuProducerPlanReport* report) +{ + if (tasks == nullptr) { + ResetReport(report); + return Fail(report, "missing output task vector"); + } + + int ret = TileXRCcuValidateProducerPlan(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + tasks->clear(); + tasks->reserve(plan.taskWindows.size()); + for (const auto& window : plan.taskWindows) { + TileXRCcuTask task; + task.dieId = window.dieId; + task.missionId = plan.mission.missionId; + task.timeout = TILEXR_CCU_DEFAULT_TASK_TIMEOUT_SEC; + task.instStartId = window.instStartId; + task.instCnt = window.instCnt; + task.key = plan.mission.key; + task.argSize = window.argSize; + std::copy(window.args.begin(), window.args.end(), task.args); + tasks->push_back(task); + } + + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildMicrocode( + const TileXRCcuProducerPlan& plan, + TileXRCcuProgram* program, + TileXRCcuProducerPlanReport* report) +{ + if (program == nullptr) { + ResetReport(report); + return Fail(report, "missing output CCU program"); + } + + int ret = TileXRCcuValidateProducerPlan(plan, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + program->sqeLoad.clear(); + program->sync.clear(); + if (plan.taskWindows.size() > 1) { + if (AppendSqeLoadProgram(plan, TILEXR_CCU_SQE_ARGS_LEN, &program->sqeLoad, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + + std::vector barrierSpecs; + barrierSpecs.reserve(plan.syncResources.size()); + for (const auto& resource : plan.syncResources) { + TileXRCcuBarrierSyncSpec spec; + spec.remoteXn = resource.remoteXn; + spec.localXn = resource.localXn; + spec.channelId = resource.channelId; + spec.remoteNotifyCke = resource.notifyCke; + spec.remoteNotifyMask = EffectiveRemoteNotifyMask(resource); + spec.localWaitCke = EffectiveLocalWaitCke(resource); + spec.localWaitMask = EffectiveLocalWaitMask(resource); + spec.sourceCke = resource.sourceCke; + spec.sourceCkeMask = EffectiveSourceCkeMask(resource); + spec.clearLocalWait = true; + barrierSpecs.push_back(spec); + } + + TileXRCcuBarrierProgramReport barrierReport; + if (AppendHcommStyleTask1Prelude(plan, &program->sync, report) != TILEXR_SUCCESS) { + program->sqeLoad.clear(); + program->sync.clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + std::vector barrierProgram; + if (TileXRCcuBuildBarrierProgram(barrierSpecs, &barrierProgram, &barrierReport, plan.barrierMode) != + TILEXR_SUCCESS) { + program->sqeLoad.clear(); + program->sync.clear(); + return Fail(report, barrierReport.message.empty() ? "failed to build sync microcode" : barrierReport.message); + } + program->sync.insert(program->sync.end(), barrierProgram.begin(), barrierProgram.end()); + + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_producer_plan.h b/src/comm/ccu/tilexr_ccu_producer_plan.h new file mode 100644 index 0000000..af4403c --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_producer_plan.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_PRODUCER_PLAN_H +#define TILEXR_CCU_PRODUCER_PLAN_H + +#include "ccu/tilexr_ccu_barrier_program.h" +#include "ccu/tilexr_ccu_runtime.h" + +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuRange { + uint8_t dieId = 0; + uint16_t startId = 0; + uint16_t num = 0; +}; + +struct TileXRCcuMission { + uint8_t dieId = 0; + uint8_t missionId = 0; + uint32_t key = 0; + bool installed = false; +}; + +struct TileXRCcuInstructionWindow { + uint8_t dieId = 0; + uint16_t repositoryStartId = 0; + uint16_t repositoryCount = 0; + uint16_t missionStartId = 0; + uint16_t missionCount = 0; +}; + +struct TileXRCcuSyncResource { + uint8_t dieId = 0; + uint16_t localXn = 0; + uint16_t remoteXn = 0; + uint16_t notifyCke = 0; + uint16_t channelId = 0; + uint16_t bindingCount = 0; + uint16_t localWaitCke = 0; + uint16_t localWaitMask = 1; + uint16_t remoteNotifyMask = 1; + uint16_t sourceCke = 0; + uint16_t sourceCkeMask = 0xffff; +}; + +struct TileXRCcuTaskWindow { + uint8_t dieId = 0; + uint16_t instStartId = 0; + uint16_t instCnt = 0; + uint32_t argSize = 0; + std::vector args; +}; + +struct TileXRCcuProducerPlan { + TileXRCcuMission mission; + TileXRCcuRange kernelLocalXn; + TileXRCcuRange kernelLocalGsa; + TileXRCcuRange kernelLocalCke; + TileXRCcuRange kernelLocalMission; + TileXRCcuInstructionWindow instructionWindow; + std::vector syncResources; + std::vector taskWindows; + TileXRCcuBarrierMode barrierMode = TileXRCcuBarrierMode::SyncXn; +}; + +struct TileXRCcuProgram { + std::vector sqeLoad; + std::vector sync; +}; + +struct TileXRCcuProducerPlanReport { + uint32_t syncResourceCount = 0; + uint32_t taskCount = 0; + uint32_t instructionCount = 0; + std::string message; +}; + +int TileXRCcuValidateProducerPlan(const TileXRCcuProducerPlan& plan, TileXRCcuProducerPlanReport* report); + +int TileXRCcuBuildTasks( + const TileXRCcuProducerPlan& plan, + std::vector* tasks, + TileXRCcuProducerPlanReport* report); + +int TileXRCcuBuildMicrocode( + const TileXRCcuProducerPlan& plan, + TileXRCcuProgram* program, + TileXRCcuProducerPlanReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_PRODUCER_PLAN_H diff --git a/src/comm/ccu/tilexr_ccu_provider.cpp b/src/comm/ccu/tilexr_ccu_provider.cpp new file mode 100644 index 0000000..d42265b --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_provider.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_provider.h" + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuProviderReport* report) +{ + if (report == nullptr) { + return; + } + report->taskCount = 0; + report->repositoryCount = 0; + report->installedInstructionCount = 0; + report->evidenceBitCount = 0; + report->publicVerifiedEvidenceCount = 0; + report->legacyEvidenceCount = 0; + report->publicCandidateEvidenceCount = 0; + report->privateObservedEvidenceCount = 0; + report->missingEvidenceCount = 0; + report->submitReady = false; + report->message.clear(); +} + +int Fail(TileXRCcuProviderReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +const char* EvidenceKindName(TileXRCcuEvidenceKind kind) +{ + switch (kind) { + case TileXRCcuEvidenceKind::LegacyBoolean: + return "legacy"; + case TileXRCcuEvidenceKind::PublicVerified: + return "public verified"; + case TileXRCcuEvidenceKind::PublicCandidate: + return "public candidate"; + case TileXRCcuEvidenceKind::PrivateObserved: + return "private observed"; + case TileXRCcuEvidenceKind::Missing: + return "missing"; + default: + return "unknown"; + } +} + +const char* EvidenceSurfaceName(TileXRCcuEvidenceSurface surface) +{ + switch (surface) { + case TileXRCcuEvidenceSurface::Unspecified: + return "unspecified surface"; + case TileXRCcuEvidenceSurface::PublicInstallProvider: + return "public install provider"; + case TileXRCcuEvidenceSurface::LowerLayerResourceHelper: + return "lower-layer resource helper"; + case TileXRCcuEvidenceSurface::PrivateProducerObservation: + return "private producer observation"; + default: + return "unknown surface"; + } +} + +std::string EvidenceMessage( + const std::string& label, + const TileXRCcuEvidenceSource& source, + const std::string& suffix) +{ + std::string message = label + " " + suffix; + if (source.kind != TileXRCcuEvidenceKind::LegacyBoolean) { + message += " (" + std::string(EvidenceKindName(source.kind)); + message += ", " + std::string(EvidenceSurfaceName(source.surface)); + if (!source.source.empty()) { + message += ": " + source.source; + } + if (!source.detail.empty()) { + message += "; " + source.detail; + } + message += ")"; + } + return message; +} + +int ValidateEvidenceSource( + const std::string& label, + bool installed, + const TileXRCcuEvidenceSource& source, + TileXRCcuProviderReport* report) +{ + if (!installed || source.kind == TileXRCcuEvidenceKind::Missing) { + return Fail(report, EvidenceMessage(label, source, "hardware install evidence is missing")); + } + if (source.kind == TileXRCcuEvidenceKind::LegacyBoolean || + source.kind == TileXRCcuEvidenceKind::PublicVerified) { + return TILEXR_SUCCESS; + } + if (source.kind == TileXRCcuEvidenceKind::PublicCandidate) { + return Fail(report, EvidenceMessage(label, source, "evidence is only a public candidate")); + } + if (source.kind == TileXRCcuEvidenceKind::PrivateObserved) { + return Fail(report, EvidenceMessage(label, source, "evidence is private observed")); + } + return Fail(report, EvidenceMessage(label, source, "evidence kind is not accepted")); +} + +void CountEvidenceKind(bool installed, const TileXRCcuEvidenceSource& source, TileXRCcuProviderReport* report) +{ + if (report == nullptr) { + return; + } + ++report->evidenceBitCount; + if (!installed || source.kind == TileXRCcuEvidenceKind::Missing) { + ++report->missingEvidenceCount; + return; + } + switch (source.kind) { + case TileXRCcuEvidenceKind::LegacyBoolean: + ++report->legacyEvidenceCount; + break; + case TileXRCcuEvidenceKind::PublicVerified: + ++report->publicVerifiedEvidenceCount; + break; + case TileXRCcuEvidenceKind::PublicCandidate: + ++report->publicCandidateEvidenceCount; + break; + case TileXRCcuEvidenceKind::PrivateObserved: + ++report->privateObservedEvidenceCount; + break; + case TileXRCcuEvidenceKind::Missing: + ++report->missingEvidenceCount; + break; + default: + ++report->missingEvidenceCount; + break; + } +} + +void CountInstallEvidence(const TileXRCcuHardwareInstallEvidence& evidence, TileXRCcuProviderReport* report) +{ + CountEvidenceKind(evidence.missionInstalled, evidence.missionSource, report); + CountEvidenceKind(evidence.repositoryInstalled, evidence.repositorySource, report); + CountEvidenceKind(evidence.localXnInstalled, evidence.localXnSource, report); + CountEvidenceKind(evidence.remoteXnBound, evidence.remoteXnSource, report); + CountEvidenceKind(evidence.notifyCkeInstalled, evidence.notifyCkeSource, report); + CountEvidenceKind(evidence.channelBindingsInstalled, evidence.channelBindingSource, report); +} + +void FillReadyReport(const TileXRCcuLaunchPackage& package, TileXRCcuProviderReport* report) +{ + if (report == nullptr) { + return; + } + report->taskCount = static_cast(package.tasks.size()); + report->repositoryCount = package.repository.repositoryCount; + report->installedInstructionCount = + static_cast(package.repository.sqeLoadCount + package.repository.syncCount); + report->submitReady = true; + report->message = "ok"; +} + +int ValidatePackageShape(const TileXRCcuLaunchPackage& package, TileXRCcuProviderReport* report) +{ + if (package.tasks.empty()) { + return Fail(report, "missing CCU launch tasks in provider package"); + } + if (package.repository.instructions.empty()) { + return Fail(report, "missing CCU repository image in provider package"); + } + if (package.program.sync.empty()) { + return Fail(report, "missing generated CCU microcode in provider package"); + } + return TILEXR_SUCCESS; +} + +bool RequiresRemoteXnBindingEvidence(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXn || + mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +bool RequiresChannelBindingEvidence(TileXRCcuBarrierMode mode) +{ + return mode != TileXRCcuBarrierMode::LocalCke && + mode != TileXRCcuBarrierMode::LocalCkePostOnly; +} + +int ValidateInstallEvidence( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + TileXRCcuProviderReport* report) +{ + CountInstallEvidence(evidence, report); + if (ValidateEvidenceSource("mission/key", evidence.missionInstalled, evidence.missionSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateEvidenceSource("repository", evidence.repositoryInstalled, evidence.repositorySource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateEvidenceSource("local XN", evidence.localXnInstalled, evidence.localXnSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode) && + ValidateEvidenceSource("remote XN binding", evidence.remoteXnBound, evidence.remoteXnSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateEvidenceSource("notify CKE install", evidence.notifyCkeInstalled, evidence.notifyCkeSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresChannelBindingEvidence(package.plan.barrierMode) && + ValidateEvidenceSource( + "channel binding", evidence.channelBindingsInstalled, evidence.channelBindingSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int ValidateSubmitEvidenceSource( + const std::string& label, + const TileXRCcuLaunchPackage& package, + bool installed, + const TileXRCcuEvidenceSource& source, + bool endpointRouteRequired, + TileXRCcuProviderReport* report) +{ + if (!installed || source.kind != TileXRCcuEvidenceKind::PublicVerified) { + return Fail(report, EvidenceMessage(label, source, "submit requires public verified evidence")); + } + if (source.source.empty() || source.detail.empty()) { + return Fail(report, EvidenceMessage(label, source, "public verified evidence source/detail required")); + } + if (source.surface != TileXRCcuEvidenceSurface::PublicInstallProvider) { + return Fail(report, EvidenceMessage( + label, source, "submit requires public install provider evidence")); + } + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (package.installScope.packageFingerprint == 0 || + package.installScope.packageFingerprint != packageFingerprint || + package.installScope.provider.empty()) { + return Fail(report, EvidenceMessage(label, source, "launch install scope is not bound")); + } + if (source.packageFingerprint == 0 || source.packageFingerprint != packageFingerprint) { + return Fail(report, EvidenceMessage(label, source, "package fingerprint mismatch")); + } + if (source.deviceId != package.installScope.deviceId) { + return Fail(report, EvidenceMessage(label, source, "device scope mismatch")); + } + if (source.rank != package.installScope.rank) { + return Fail(report, EvidenceMessage(label, source, "rank scope mismatch")); + } + if (source.provider.empty() || source.provider != package.installScope.provider) { + return Fail(report, EvidenceMessage(label, source, "provider scope mismatch")); + } + if (source.installAttemptReceiptId == 0) { + return Fail(report, EvidenceMessage(label, source, "install attempt receipt is missing")); + } + if (endpointRouteRequired && !source.endpointRouteVerified) { + return Fail(report, EvidenceMessage(label, source, "submit requires verified endpoint route evidence")); + } + return TILEXR_SUCCESS; +} + +int ValidateSameInstallAttemptReceipt( + const TileXRCcuEvidenceSource& expected, + const std::string& label, + const TileXRCcuEvidenceSource& source, + TileXRCcuProviderReport* report) +{ + if (source.installAttemptReceiptId != expected.installAttemptReceiptId) { + return Fail(report, EvidenceMessage(label, source, "install attempt receipt mismatch")); + } + return TILEXR_SUCCESS; +} + +int ValidateSubmitEvidence( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + TileXRCcuProviderReport* report) +{ + if (ValidateSubmitEvidenceSource( + "mission/key", package, evidence.missionInstalled, evidence.missionSource, false, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateSubmitEvidenceSource( + "repository", package, evidence.repositoryInstalled, evidence.repositorySource, false, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateSubmitEvidenceSource( + "local XN", package, evidence.localXnInstalled, evidence.localXnSource, false, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode) && + ValidateSubmitEvidenceSource( + "remote XN binding", package, evidence.remoteXnBound, evidence.remoteXnSource, true, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateSubmitEvidenceSource( + "notify CKE install", package, evidence.notifyCkeInstalled, evidence.notifyCkeSource, false, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresChannelBindingEvidence(package.plan.barrierMode) && + ValidateSubmitEvidenceSource( + "channel binding", package, evidence.channelBindingsInstalled, evidence.channelBindingSource, true, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const TileXRCcuEvidenceSource& expected = evidence.missionSource; + if (ValidateSameInstallAttemptReceipt(expected, "repository", evidence.repositorySource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateSameInstallAttemptReceipt(expected, "local XN", evidence.localXnSource, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresRemoteXnBindingEvidence(package.plan.barrierMode) && + ValidateSameInstallAttemptReceipt(expected, "remote XN binding", evidence.remoteXnSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateSameInstallAttemptReceipt(expected, "notify CKE install", evidence.notifyCkeSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (RequiresChannelBindingEvidence(package.plan.barrierMode) && + ValidateSameInstallAttemptReceipt(expected, "channel binding", evidence.channelBindingSource, report) != + TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +} // namespace + +int TileXRCcuValidateHardwareInstall( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + TileXRCcuProviderReport* report) +{ + ResetReport(report); + if (ValidatePackageShape(package, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateInstallEvidence(package, evidence, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (report != nullptr) { + report->taskCount = static_cast(package.tasks.size()); + report->repositoryCount = package.repository.repositoryCount; + report->installedInstructionCount = + static_cast(package.repository.sqeLoadCount + package.repository.syncCount); + report->submitReady = false; + report->message = "hardware install evidence is validate-compatible"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuPrepareSubmitTasks( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + std::vector* submitTasks, + TileXRCcuProviderReport* report) +{ + ResetReport(report); + if (submitTasks == nullptr) { + return Fail(report, "missing output submit task vector"); + } + submitTasks->clear(); + + if (TileXRCcuValidateHardwareInstall(package, evidence, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (report != nullptr) { + report->submitReady = false; + } + if (ValidateSubmitEvidence(package, evidence, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *submitTasks = package.tasks; + FillReadyReport(package, report); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_provider.h b/src/comm/ccu/tilexr_ccu_provider.h new file mode 100644 index 0000000..2a96436 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_provider.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_PROVIDER_H +#define TILEXR_CCU_PROVIDER_H + +#include "ccu/tilexr_ccu_launch_package.h" + +#include +#include +#include + +namespace TileXR { + +enum class TileXRCcuEvidenceKind : uint8_t { + LegacyBoolean = 0, + PublicVerified = 1, + PublicCandidate = 2, + PrivateObserved = 3, + Missing = 4, +}; + +enum class TileXRCcuEvidenceSurface : uint8_t { + Unspecified = 0, + PublicInstallProvider = 1, + LowerLayerResourceHelper = 2, + PrivateProducerObservation = 3, +}; + +struct TileXRCcuEvidenceSource { + TileXRCcuEvidenceKind kind = TileXRCcuEvidenceKind::LegacyBoolean; + TileXRCcuEvidenceSurface surface = TileXRCcuEvidenceSurface::Unspecified; + uint64_t packageFingerprint = 0; + uint32_t deviceId = 0; + uint32_t rank = 0; + std::string provider; + uint64_t installAttemptReceiptId = 0; + bool endpointRouteVerified = false; + std::string source; + std::string detail; +}; + +struct TileXRCcuHardwareInstallEvidence { + bool missionInstalled = false; + bool repositoryInstalled = false; + bool localXnInstalled = false; + bool remoteXnBound = false; + bool notifyCkeInstalled = false; + bool channelBindingsInstalled = false; + TileXRCcuEvidenceSource missionSource; + TileXRCcuEvidenceSource repositorySource; + TileXRCcuEvidenceSource localXnSource; + TileXRCcuEvidenceSource remoteXnSource; + TileXRCcuEvidenceSource notifyCkeSource; + TileXRCcuEvidenceSource channelBindingSource; +}; + +struct TileXRCcuProviderReport { + uint32_t taskCount = 0; + uint32_t repositoryCount = 0; + uint32_t installedInstructionCount = 0; + uint32_t evidenceBitCount = 0; + uint32_t publicVerifiedEvidenceCount = 0; + uint32_t legacyEvidenceCount = 0; + uint32_t publicCandidateEvidenceCount = 0; + uint32_t privateObservedEvidenceCount = 0; + uint32_t missingEvidenceCount = 0; + bool submitReady = false; + std::string message; +}; + +int TileXRCcuValidateHardwareInstall( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + TileXRCcuProviderReport* report); + +int TileXRCcuPrepareSubmitTasks( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + std::vector* submitTasks, + TileXRCcuProviderReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_PROVIDER_H diff --git a/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp new file mode 100644 index 0000000..f364290 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuRaCustomChannelProviderReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuRaCustomChannelProviderReport{}; + } +} + +int Fail(TileXRCcuRaCustomChannelProviderReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +void FillReport( + uint32_t devicePhyId, + bool initialized, + const std::string& message, + TileXRCcuRaCustomChannelProviderReport* report) +{ + if (report == nullptr) { + return; + } + report->devicePhyId = devicePhyId; + report->initialized = initialized; + report->message = message; +} + +} // namespace + +int TileXRCcuRaCustomChannelProvider::Init( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelFunc raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report) +{ + TileXRCcuRaCustomChannelInvoker invoker; + if (raCustomChannel != nullptr) { + invoker = + [raCustomChannel]( + TileXRCcuRaInfo info, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out) -> int { + return raCustomChannel( + info, + const_cast(&in), + out); + }; + } + return InitCallable(devicePhyId, invoker, report); +} + +int TileXRCcuRaCustomChannelProvider::InitCallable( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelInvoker raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report) +{ + ResetReport(report); + if (!raCustomChannel) { + initialized_ = false; + return Fail(report, "missing RA custom channel function"); + } + devicePhyId_ = devicePhyId; + raCustomChannel_ = raCustomChannel; + initialized_ = true; + FillReport(devicePhyId_, initialized_, "ok", report); + return TILEXR_SUCCESS; +} + +int TileXRCcuRaCustomChannelProvider::CreateAdapter( + TileXRCcuDriverAdapter* adapter, + TileXRCcuDriverAdapterReport* report) +{ + if (adapter == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!initialized_ || !raCustomChannel_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + return adapter->Init(devicePhyId_, &TileXRCcuRaCustomChannelProvider::AdapterCallback, this, report); +} + +int TileXRCcuRaCustomChannelProvider::AdapterCallback( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) +{ + auto* provider = static_cast(userData); + if (provider == nullptr || !provider->raCustomChannel_ || out == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXRCcuRaInfo info {}; + info.mode = TILEXR_CCU_NETWORK_OFFLINE; + info.phyId = devicePhyId; + return provider->raCustomChannel_( + info, + in, + out); +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h new file mode 100644 index 0000000..48f9984 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_RA_CUSTOM_CHANNEL_PROVIDER_H +#define TILEXR_CCU_RA_CUSTOM_CHANNEL_PROVIDER_H + +#include "ccu/tilexr_ccu_driver_adapter.h" +#include "ccu/tilexr_ccu_hccp_types.h" + +#include +#include +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuRaCustomChannelProviderReport { + uint32_t devicePhyId = 0; + bool initialized = false; + std::string message; +}; + +class TileXRCcuRaCustomChannelProvider { +private: + using TileXRCcuRaCustomChannelInvoker = std::function; + +public: + int Init( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelFunc raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report); + + template + int Init( + uint32_t devicePhyId, + int (*raCustomChannel)(RaInfoT, void*, void*), + TileXRCcuRaCustomChannelProviderReport* report) + { + if (raCustomChannel == nullptr) { + return InitCallable(devicePhyId, TileXRCcuRaCustomChannelInvoker {}, report); + } + TileXRCcuRaCustomChannelInvoker invoker = + [raCustomChannel]( + TileXRCcuRaInfo info, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out) -> int { + RaInfoT compatInfo {}; + const size_t copyBytes = sizeof(compatInfo) < sizeof(info) ? sizeof(compatInfo) : sizeof(info); + std::memcpy(&compatInfo, &info, copyBytes); + return raCustomChannel(compatInfo, const_cast(&in), out); + }; + return InitCallable(devicePhyId, invoker, report); + } + + int CreateAdapter(TileXRCcuDriverAdapter* adapter, TileXRCcuDriverAdapterReport* report); + +private: + int InitCallable( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelInvoker raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report); + + static int AdapterCallback( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData); + + uint32_t devicePhyId_ = 0; + TileXRCcuRaCustomChannelInvoker raCustomChannel_; + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_RA_CUSTOM_CHANNEL_PROVIDER_H diff --git a/src/comm/ccu/tilexr_ccu_repository.cpp b/src/comm/ccu/tilexr_ccu_repository.cpp new file mode 100644 index 0000000..4b9b6bc --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_repository.cpp @@ -0,0 +1,894 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_repository.h" + +#if defined(__has_include) +#if __has_include() +#define TILEXR_CCU_HAVE_ACL_RT_HEADER 1 +#include +#else +#define TILEXR_CCU_HAVE_ACL_RT_HEADER 0 +#endif +#else +#define TILEXR_CCU_HAVE_ACL_RT_HEADER 1 +#include +#endif + +#if defined(__has_include) +#if __has_include() +#define TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER 1 +#include +#else +#define TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER 0 +#endif +#else +#define TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER 1 +#include +#endif + +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr uint16_t TILEXR_CCU_ACL_MODULE3_ID = 3U; +constexpr uint16_t TILEXR_CCU_RT_HBM_MODULE_ID = 0U; + +struct TileXRCcuUploadReadbackDiagnostic { + bool attempted = false; + bool ok = false; + int ret = 0; + uint64_t bytes = 0; + uint64_t fnv1a64 = 0; + uint32_t mismatchCount = 0; + std::string firstInstructionWords; + std::string lastInstructionWords; +}; + +void ResetReport(TileXRCcuRepositoryReport* report) +{ + if (report == nullptr) { + return; + } + report->repositoryCount = 0; + report->installedInstructionCount = 0; + report->sqeLoadOffset = 0; + report->syncOffset = 0; + report->repositoryUploaded = false; + report->repositoryInstalled = false; + report->message.clear(); +} + +int Fail( + TileXRCcuRepositoryReport* report, + const std::string& message, + int code = TILEXR_ERROR_PARA_CHECK_FAIL) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +bool ContainsWindow(uint16_t outerStart, uint16_t outerCount, uint16_t innerStart, uint16_t innerCount) +{ + if (outerCount == 0 || innerCount == 0) { + return false; + } + const uint32_t outerBegin = outerStart; + const uint32_t outerEnd = outerBegin + outerCount; + const uint32_t innerBegin = innerStart; + const uint32_t innerEnd = innerBegin + innerCount; + return innerBegin >= outerBegin && innerEnd <= outerEnd; +} + +uint16_t OffsetFrom(uint16_t base, uint16_t id) +{ + return static_cast(static_cast(id) - base); +} + +int ValidateProgramShape( + const TileXRCcuProducerPlan& plan, + const TileXRCcuProgram& program, + TileXRCcuRepositoryReport* report) +{ + if (program.sync.empty()) { + return Fail(report, "missing sync microcode for repository image"); + } + if (program.sqeLoad.empty()) { + if (plan.taskWindows.size() != 1) { + return Fail(report, "pure sync repository image requires exactly one task window"); + } + const auto& syncTask = plan.taskWindows[0]; + if (syncTask.instCnt < program.sync.size()) { + return Fail(report, "sync task window is too small for generated sync microcode"); + } + return TILEXR_SUCCESS; + } + if (plan.taskWindows.size() < 2) { + return Fail(report, "repository image requires SQE and sync task windows"); + } + + const auto& sqeTask = plan.taskWindows[0]; + const auto& syncTask = plan.taskWindows[1]; + if (sqeTask.instCnt != program.sqeLoad.size()) { + return Fail(report, "SQE task window does not match generated SQE microcode"); + } + if (syncTask.instStartId != static_cast(sqeTask.instStartId) + sqeTask.instCnt) { + return Fail(report, "sync task window must start immediately after SQE microcode"); + } + if (syncTask.instCnt < program.sync.size()) { + return Fail(report, "sync task window is too small for generated sync microcode"); + } + return TILEXR_SUCCESS; +} + +int ValidateRepositoryWindow(const TileXRCcuProducerPlan& plan, TileXRCcuRepositoryReport* report) +{ + const auto& window = plan.instructionWindow; + if (!ContainsWindow( + window.repositoryStartId, window.repositoryCount, window.missionStartId, window.missionCount)) { + return Fail(report, "mission instruction window is outside the repository image"); + } + for (const auto& task : plan.taskWindows) { + if (task.dieId != window.dieId) { + return Fail(report, "task die does not match repository image die"); + } + if (!ContainsWindow(window.missionStartId, window.missionCount, task.instStartId, task.instCnt)) { + return Fail(report, "task instruction window is outside the mission image"); + } + if (!ContainsWindow(window.repositoryStartId, window.repositoryCount, task.instStartId, task.instCnt)) { + return Fail(report, "task instruction window is outside the repository image"); + } + } + return TILEXR_SUCCESS; +} + +void FillImageHeader(const TileXRCcuInstructionWindow& window, TileXRCcuRepositoryImage* image) +{ + image->dieId = window.dieId; + image->repositoryStartId = window.repositoryStartId; + image->repositoryCount = window.repositoryCount; + image->missionStartId = window.missionStartId; + image->missionCount = window.missionCount; + image->missionOffset = OffsetFrom(window.repositoryStartId, window.missionStartId); +} + +void FillReport(const TileXRCcuRepositoryImage& image, TileXRCcuRepositoryReport* report) +{ + if (report == nullptr) { + return; + } + report->repositoryCount = image.repositoryCount; + report->installedInstructionCount = static_cast(image.sqeLoadCount + image.syncCount); + report->sqeLoadOffset = image.sqeLoadOffset; + report->syncOffset = image.syncOffset; + report->repositoryUploaded = false; + report->repositoryInstalled = false; + report->message = "ok"; +} + +int BuildRepositoryPaddingInstruction(const TileXRCcuProducerPlan& plan, TileXRCcuInstr* instr, + TileXRCcuRepositoryReport* report) +{ + if (plan.kernelLocalXn.startId == 0 || + TileXRCcuEncodeLoadImdToXn(plan.kernelLocalXn.startId, 0, 0, instr) != TILEXR_SUCCESS) { + return Fail(report, "failed to build valid repository padding instruction"); + } + return TILEXR_SUCCESS; +} + +int ValidateMemoryOps(const TileXRCcuDeviceMemoryOps& memoryOps, TileXRCcuRepositoryReport* report) +{ + if (memoryOps.alloc == nullptr) { + return Fail(report, "missing CCU repository device allocation hook"); + } + if (memoryOps.copyHostToDevice == nullptr) { + return Fail(report, "missing CCU repository host-to-device copy hook"); + } + if (memoryOps.free == nullptr) { + return Fail(report, "missing CCU repository device free hook"); + } + return TILEXR_SUCCESS; +} + +int ValidateInstallImage(const TileXRCcuRepositoryImage& image, TileXRCcuRepositoryReport* report) +{ + if (image.instructions.empty()) { + return Fail(report, "missing CCU repository instruction image"); + } + if (image.missionCount == 0) { + return Fail(report, "missing CCU repository mission instruction window"); + } + if (image.missionOffset >= image.instructions.size() || + static_cast(image.missionOffset) + image.missionCount > image.instructions.size()) { + return Fail(report, "CCU repository mission window is outside instruction image"); + } + if (image.missionStartId == 0) { + return Fail(report, "missing CCU repository mission start instruction id"); + } + return TILEXR_SUCCESS; +} + +void FillInstallReport(const TileXRCcuRepositoryInstallReceipt& receipt, TileXRCcuRepositoryReport* report) +{ + if (report == nullptr) { + return; + } + report->repositoryCount = receipt.instructionCount; + report->installedInstructionCount = receipt.instructionCount; + report->sqeLoadOffset = 0; + report->syncOffset = 0; + report->repositoryUploaded = receipt.uploaded; + report->repositoryInstalled = receipt.installed; + report->message = "ok"; +} + +bool FitsSizeT(uint64_t bytes) +{ + return bytes <= static_cast(std::numeric_limits::max()); +} + +uint16_t InstallStartId(const TileXRCcuRepositoryImage& image, const TileXRCcuRepositoryInstallOptions& options) +{ + return options.window == TileXRCcuRepositoryInstallWindow::FullRepository ? + image.repositoryStartId : + image.missionStartId; +} + +uint16_t InstallInstructionCount( + const TileXRCcuRepositoryImage& image, + const TileXRCcuRepositoryInstallOptions& options) +{ + return options.window == TileXRCcuRepositoryInstallWindow::FullRepository ? + image.repositoryCount : + image.missionCount; +} + +uint16_t InstallImageOffset(const TileXRCcuRepositoryImage& image, const TileXRCcuRepositoryInstallOptions& options) +{ + return options.window == TileXRCcuRepositoryInstallWindow::FullRepository ? 0 : image.missionOffset; +} + +uint32_t InstallCustomChannelDataLen( + uint64_t instructionBytes, + const TileXRCcuRepositoryInstallOptions& options) +{ + return options.dataLenMode == TileXRCcuRepositoryInstallDataLenMode::DescriptorBytes ? + static_cast(sizeof(TileXRCcuInstrInfo)) : + static_cast(instructionBytes); +} + +const char* InstallWindowText(TileXRCcuRepositoryInstallWindow window) +{ + return window == TileXRCcuRepositoryInstallWindow::FullRepository ? "full_repository" : "mission"; +} + +const char* InstallDataLenModeText(TileXRCcuRepositoryInstallDataLenMode mode) +{ + return mode == TileXRCcuRepositoryInstallDataLenMode::DescriptorBytes ? + "descriptor_bytes" : + "instruction_bytes"; +} + +uint64_t MixFnv1aByte(uint64_t hash, uint8_t value) +{ + constexpr uint64_t prime = 1099511628211ULL; + hash ^= value; + hash *= prime; + return hash; +} + +uint64_t BuildInstructionFnv1a64(const TileXRCcuInstr* instructions, uint16_t count) +{ + uint64_t hash = 1469598103934665603ULL; + for (uint16_t i = 0; i < count; ++i) { + for (uint64_t word : instructions[i].words) { + for (uint32_t byte = 0; byte < 8U; ++byte) { + hash = MixFnv1aByte(hash, static_cast((word >> (byte * 8U)) & 0xffU)); + } + } + } + return hash; +} + +std::string FormatInstructionWords(const TileXRCcuInstr& instr) +{ + std::ostringstream out; + out << std::hex << std::setfill('0'); + for (uint32_t i = 0; i < 4U; ++i) { + if (i != 0) { + out << ","; + } + out << "0x" << std::setw(16) << instr.words[i]; + } + return out.str(); +} + +uint32_t CountInstructionMismatches( + const TileXRCcuInstr* expected, + const TileXRCcuInstr* actual, + uint16_t count) +{ + uint32_t mismatches = 0; + for (uint16_t i = 0; i < count; ++i) { + if (expected[i].words[0] != actual[i].words[0] || + expected[i].words[1] != actual[i].words[1] || + expected[i].words[2] != actual[i].words[2] || + expected[i].words[3] != actual[i].words[3]) { + ++mismatches; + } + } + return mismatches; +} + +const char* UploadReadbackStatusText(const TileXRCcuUploadReadbackDiagnostic& diagnostic) +{ + if (!diagnostic.attempted) { + return "skipped"; + } + if (diagnostic.ret != 0) { + return "failed"; + } + if (!diagnostic.ok) { + return "mismatch"; + } + return "ok"; +} + +void FillSuccessfulReadbackDiagnostic( + const std::vector& readback, + const TileXRCcuInstr* expectedInstructions, + uint16_t instructionCount, + TileXRCcuUploadReadbackDiagnostic* diagnostic) +{ + if (diagnostic == nullptr || readback.empty() || expectedInstructions == nullptr || instructionCount == 0) { + return; + } + diagnostic->fnv1a64 = BuildInstructionFnv1a64(readback.data(), instructionCount); + diagnostic->firstInstructionWords = FormatInstructionWords(readback.front()); + diagnostic->lastInstructionWords = FormatInstructionWords(readback.back()); + diagnostic->mismatchCount = CountInstructionMismatches(expectedInstructions, readback.data(), instructionCount); + diagnostic->ok = diagnostic->mismatchCount == 0; +} + +void BuildUploadReadbackDiagnostic( + const TileXRCcuDeviceMemoryOps& memoryOps, + void* devicePtr, + uint64_t instructionBytes, + const TileXRCcuInstr* expectedInstructions, + uint16_t instructionCount, + void* memoryUserData, + TileXRCcuUploadReadbackDiagnostic* diagnostic) +{ + if (diagnostic == nullptr) { + return; + } + *diagnostic = TileXRCcuUploadReadbackDiagnostic {}; + if (memoryOps.copyDeviceToHost == nullptr) { + return; + } + diagnostic->attempted = true; + diagnostic->bytes = instructionBytes; + if (devicePtr == nullptr || expectedInstructions == nullptr || instructionCount == 0 || + !FitsSizeT(instructionBytes)) { + diagnostic->ret = TILEXR_ERROR_PARA_CHECK_FAIL; + return; + } + + std::vector readback(instructionCount); + diagnostic->ret = memoryOps.copyDeviceToHost( + readback.data(), + instructionBytes, + devicePtr, + instructionBytes, + memoryUserData); + if (diagnostic->ret != 0) { + return; + } + + FillSuccessfulReadbackDiagnostic(readback, expectedInstructions, instructionCount, diagnostic); +} + +void BuildDriverInstructionReadbackDiagnostic( + const TileXRCcuDriverAdapter& adapter, + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t instructionBytes, + const TileXRCcuInstr* expectedInstructions, + TileXRCcuUploadReadbackDiagnostic* diagnostic) +{ + if (diagnostic == nullptr) { + return; + } + *diagnostic = TileXRCcuUploadReadbackDiagnostic {}; + diagnostic->attempted = true; + diagnostic->bytes = instructionBytes; + if (expectedInstructions == nullptr || instructionCount == 0 || !FitsSizeT(instructionBytes)) { + diagnostic->ret = TILEXR_ERROR_PARA_CHECK_FAIL; + return; + } + + std::vector readback(instructionCount); + uint16_t readOffset = 0; + while (readOffset < instructionCount) { + const uint32_t batch = std::min( + TILEXR_CCU_MAX_DATA_ARRAY_SIZE, + static_cast(instructionCount - readOffset)); + TileXRCcuDriverAdapterReport driverReport; + diagnostic->ret = adapter.ReadInstructions( + dieId, + static_cast(instructionStartId + readOffset), + readback.data() + readOffset, + batch, + batch * TILEXR_CCU_INSTRUCTION_BYTES, + &driverReport); + if (diagnostic->ret != TILEXR_SUCCESS) { + return; + } + readOffset = static_cast(readOffset + batch); + } + + FillSuccessfulReadbackDiagnostic(readback, expectedInstructions, instructionCount, diagnostic); +} + +std::string BuildInstallFailureDiagnostic( + const std::string& driverMessage, + const TileXRCcuRepositoryImage& image, + const TileXRCcuRepositoryInstallOptions& options, + uint16_t installOffset, + uint16_t installStartId, + uint16_t installCount, + uint64_t instructionBytes, + uint32_t customChannelDataLen, + uint64_t deviceInstructionAddr, + const TileXRCcuUploadReadbackDiagnostic& uploadReadback, + const TileXRCcuUploadReadbackDiagnostic& driverReadback) +{ + const TileXRCcuInstr* firstInstruction = image.instructions.data() + installOffset; + const TileXRCcuInstr* lastInstruction = firstInstruction + installCount - 1U; + const uint64_t hash = BuildInstructionFnv1a64(firstInstruction, installCount); + + std::ostringstream message; + message << "failed to install CCU repository instruction image: " << driverMessage + << " dieId=" << static_cast(image.dieId) + << " installStartId=" << installStartId + << " installCount=" << installCount + << " instructionBytes=" << instructionBytes + << " customChannelDataLen=" << customChannelDataLen + << " deviceInstructionAddr=0x" << std::hex << deviceInstructionAddr << std::dec + << " window=" << InstallWindowText(options.window) + << " dataLenMode=" << InstallDataLenModeText(options.dataLenMode) + << " firstInstructionWords=" << FormatInstructionWords(*firstInstruction) + << " lastInstructionWords=" << FormatInstructionWords(*lastInstruction) + << " instructionFnv1a64=0x" << std::hex << hash << std::dec + << " uploadReadback=" << UploadReadbackStatusText(uploadReadback); + if (uploadReadback.attempted) { + message << " uploadReadbackRet=" << uploadReadback.ret + << " uploadReadbackBytes=" << uploadReadback.bytes; + if (uploadReadback.ret == 0) { + message << " uploadReadbackFnv1a64=0x" << std::hex << uploadReadback.fnv1a64 << std::dec + << " uploadReadbackFirstInstructionWords=" << uploadReadback.firstInstructionWords + << " uploadReadbackLastInstructionWords=" << uploadReadback.lastInstructionWords + << " uploadReadbackMismatchCount=" << uploadReadback.mismatchCount; + } + } + message << " driverReadback=" << UploadReadbackStatusText(driverReadback); + if (driverReadback.attempted) { + message << " driverReadbackRet=" << driverReadback.ret + << " driverReadbackBytes=" << driverReadback.bytes; + if (driverReadback.ret == 0) { + message << " driverReadbackFnv1a64=0x" << std::hex << driverReadback.fnv1a64 << std::dec + << " driverReadbackFirstInstructionWords=" << driverReadback.firstInstructionWords + << " driverReadbackLastInstructionWords=" << driverReadback.lastInstructionWords + << " driverReadbackMismatchCount=" << driverReadback.mismatchCount; + } + } + return message.str(); +} + +int AclDeviceAlloc(uint64_t bytes, void** devicePtr, void*) +{ + if (devicePtr == nullptr || bytes == 0 || !FitsSizeT(bytes)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + return aclrtMalloc(devicePtr, static_cast(bytes), ACL_MEM_MALLOC_HUGE_FIRST); +#else + *devicePtr = nullptr; + return TILEXR_ERROR_MKIRT; +#endif +} + +int AclModule3DeviceAlloc(uint64_t bytes, void** devicePtr, void*) +{ + if (devicePtr == nullptr || bytes == 0 || !FitsSizeT(bytes)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + aclrtMallocAttrValue moduleIdValue {}; + moduleIdValue.moduleId = TILEXR_CCU_ACL_MODULE3_ID; + aclrtMallocAttribute attrs {ACL_RT_MEM_ATTR_MODULE_ID, moduleIdValue}; + aclrtMallocConfig cfg {&attrs, 1}; + return aclrtMallocWithCfg( + devicePtr, + static_cast(bytes), + static_cast(ACL_MEM_TYPE_HIGH_BAND_WIDTH | ACL_MEM_MALLOC_HUGE_FIRST), + &cfg); +#else + *devicePtr = nullptr; + return TILEXR_ERROR_MKIRT; +#endif +} + +int AclCopyHostToDevice(void* deviceDst, uint64_t deviceDstBytes, const void* hostSrc, uint64_t bytes, void*) +{ + if (deviceDst == nullptr || hostSrc == nullptr || bytes == 0 || bytes > deviceDstBytes || + !FitsSizeT(deviceDstBytes) || !FitsSizeT(bytes)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + return aclrtMemcpy( + deviceDst, + static_cast(deviceDstBytes), + hostSrc, + static_cast(bytes), + ACL_MEMCPY_HOST_TO_DEVICE); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +int AclCopyDeviceToHost(void* hostDst, uint64_t hostDstBytes, const void* deviceSrc, uint64_t bytes, void*) +{ + if (hostDst == nullptr || deviceSrc == nullptr || bytes == 0 || bytes > hostDstBytes || + !FitsSizeT(hostDstBytes) || !FitsSizeT(bytes)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + return aclrtMemcpy( + hostDst, + static_cast(hostDstBytes), + deviceSrc, + static_cast(bytes), + ACL_MEMCPY_DEVICE_TO_HOST); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +int AclDeviceFree(void* devicePtr, void*) +{ + if (devicePtr == nullptr) { + return TILEXR_SUCCESS; + } +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + return aclrtFree(devicePtr); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +int RtHbmDeviceAlloc(uint64_t bytes, void** devicePtr, void*) +{ + if (devicePtr == nullptr || bytes == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER + return rtMalloc(devicePtr, bytes, RT_MEMORY_HBM, TILEXR_CCU_RT_HBM_MODULE_ID); +#else + *devicePtr = nullptr; + return TILEXR_ERROR_MKIRT; +#endif +} + +int RtHbmCopyHostToDevice(void* deviceDst, uint64_t deviceDstBytes, const void* hostSrc, uint64_t bytes, void*) +{ + if (deviceDst == nullptr || hostSrc == nullptr || bytes == 0 || bytes > deviceDstBytes) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER + return rtMemcpy(deviceDst, deviceDstBytes, hostSrc, bytes, RT_MEMCPY_HOST_TO_DEVICE); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +int RtHbmCopyDeviceToHost(void* hostDst, uint64_t hostDstBytes, const void* deviceSrc, uint64_t bytes, void*) +{ + if (hostDst == nullptr || deviceSrc == nullptr || bytes == 0 || bytes > hostDstBytes) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } +#if TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER + return rtMemcpy(hostDst, hostDstBytes, deviceSrc, bytes, RT_MEMCPY_DEVICE_TO_HOST); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +int RtHbmDeviceFree(void* devicePtr, void*) +{ + if (devicePtr == nullptr) { + return TILEXR_SUCCESS; + } +#if TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER + return rtFree(devicePtr); +#else + return TILEXR_ERROR_MKIRT; +#endif +} + +} // namespace + +TileXRCcuDeviceMemoryOps TileXRCcuMakeAclDeviceMemoryOps() +{ + TileXRCcuDeviceMemoryOps ops; +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + ops.alloc = AclDeviceAlloc; + ops.copyHostToDevice = AclCopyHostToDevice; + ops.free = AclDeviceFree; + ops.copyDeviceToHost = AclCopyDeviceToHost; +#endif + return ops; +} + +TileXRCcuDeviceMemoryOps TileXRCcuMakeAclModule3DeviceMemoryOps() +{ + TileXRCcuDeviceMemoryOps ops; +#if TILEXR_CCU_HAVE_ACL_RT_HEADER + ops.alloc = AclModule3DeviceAlloc; + ops.copyHostToDevice = AclCopyHostToDevice; + ops.free = AclDeviceFree; + ops.copyDeviceToHost = AclCopyDeviceToHost; +#endif + return ops; +} + +TileXRCcuDeviceMemoryOps TileXRCcuMakeRtHbmDeviceMemoryOps() +{ + TileXRCcuDeviceMemoryOps ops; +#if TILEXR_CCU_HAVE_RUNTIME_MEM_HEADER + ops.alloc = RtHbmDeviceAlloc; + ops.copyHostToDevice = RtHbmCopyHostToDevice; + ops.free = RtHbmDeviceFree; + ops.copyDeviceToHost = RtHbmCopyDeviceToHost; +#endif + return ops; +} + +TileXRCcuDeviceMemoryOps TileXRCcuMakeRepositoryDeviceMemoryOps(TileXRCcuRepositoryMemoryAllocMode mode) +{ + if (mode == TileXRCcuRepositoryMemoryAllocMode::AclModule3) { + return TileXRCcuMakeAclModule3DeviceMemoryOps(); + } + if (mode == TileXRCcuRepositoryMemoryAllocMode::RtHbm) { + return TileXRCcuMakeRtHbmDeviceMemoryOps(); + } + return TileXRCcuMakeAclDeviceMemoryOps(); +} + +int TileXRCcuBuildRepositoryImage( + const TileXRCcuProducerPlan& plan, + const TileXRCcuProgram& program, + TileXRCcuRepositoryImage* image, + TileXRCcuRepositoryReport* report) +{ + ResetReport(report); + if (image == nullptr) { + return Fail(report, "missing output repository image"); + } + image->instructions.clear(); + + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + return Fail(report, planReport.message); + } + if (ValidateProgramShape(plan, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (ValidateRepositoryWindow(plan, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + FillImageHeader(plan.instructionWindow, image); + const size_t syncTaskIndex = program.sqeLoad.empty() ? 0U : 1U; + image->sqeLoadOffset = program.sqeLoad.empty() ? + 0 : + OffsetFrom(plan.instructionWindow.repositoryStartId, plan.taskWindows[0].instStartId); + image->sqeLoadCount = static_cast(program.sqeLoad.size()); + image->syncOffset = OffsetFrom(plan.instructionWindow.repositoryStartId, plan.taskWindows[syncTaskIndex].instStartId); + image->syncCount = static_cast(program.sync.size()); + + const uint32_t sqeEnd = static_cast(image->sqeLoadOffset) + image->sqeLoadCount; + const uint32_t syncEnd = static_cast(image->syncOffset) + image->syncCount; + if (sqeEnd > image->repositoryCount || syncEnd > image->repositoryCount) { + image->instructions.clear(); + return Fail(report, "generated microcode does not fit in repository image"); + } + + TileXRCcuInstr paddingInstr; + if (BuildRepositoryPaddingInstruction(plan, &paddingInstr, report) != TILEXR_SUCCESS) { + image->instructions.clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + image->instructions.assign(image->repositoryCount, paddingInstr); + std::copy(program.sqeLoad.begin(), program.sqeLoad.end(), image->instructions.begin() + image->sqeLoadOffset); + std::copy(program.sync.begin(), program.sync.end(), image->instructions.begin() + image->syncOffset); + + FillReport(*image, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuInstallRepositoryImage( + const TileXRCcuRepositoryImage& image, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + const TileXRCcuDriverAdapter& adapter, + TileXRCcuRepositoryInstallReceipt* receipt, + TileXRCcuRepositoryReport* report) +{ + TileXRCcuRepositoryInstallOptions options; + return TileXRCcuInstallRepositoryImageWithOptions( + image, + options, + memoryOps, + memoryUserData, + adapter, + receipt, + report); +} + +int TileXRCcuInstallRepositoryImageWithOptions( + const TileXRCcuRepositoryImage& image, + const TileXRCcuRepositoryInstallOptions& options, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + const TileXRCcuDriverAdapter& adapter, + TileXRCcuRepositoryInstallReceipt* receipt, + TileXRCcuRepositoryReport* report) +{ + ResetReport(report); + if (receipt == nullptr) { + return Fail(report, "missing output CCU repository install receipt"); + } + *receipt = TileXRCcuRepositoryInstallReceipt{}; + + if (ValidateMemoryOps(memoryOps, report) != TILEXR_SUCCESS || + ValidateInstallImage(image, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const uint16_t installOffset = InstallImageOffset(image, options); + const uint16_t installStartId = InstallStartId(image, options); + const uint16_t installCount = InstallInstructionCount(image, options); + if (installCount == 0 || + installOffset >= image.instructions.size() || + static_cast(installOffset) + installCount > image.instructions.size()) { + return Fail(report, "CCU repository selected install window is outside instruction image"); + } + + const uint64_t instructionBytes = static_cast(installCount) * sizeof(TileXRCcuInstr); + void* devicePtr = nullptr; + const int allocRet = memoryOps.alloc(instructionBytes, &devicePtr, memoryUserData); + if (allocRet != 0 || devicePtr == nullptr) { + return Fail(report, "failed to allocate CCU repository device instruction image", TILEXR_ERROR_MKIRT); + } + + const auto* firstInstruction = image.instructions.data() + installOffset; + const int copyRet = memoryOps.copyHostToDevice( + devicePtr, + instructionBytes, + firstInstruction, + instructionBytes, + memoryUserData); + if (copyRet != 0) { + (void)memoryOps.free(devicePtr, memoryUserData); + return Fail(report, "failed to copy CCU repository instruction image to device", TILEXR_ERROR_MKIRT); + } + + TileXRCcuRepositoryInstallReceipt result; + result.dieId = image.dieId; + result.instructionStartId = installStartId; + result.instructionCount = installCount; + result.instructionBytes = instructionBytes; + result.deviceInstructionAddr = reinterpret_cast(devicePtr); + result.deviceInstructionPtr = devicePtr; + result.uploaded = true; + + TileXRCcuUploadReadbackDiagnostic uploadReadback; + BuildUploadReadbackDiagnostic( + memoryOps, + devicePtr, + instructionBytes, + firstInstruction, + installCount, + memoryUserData, + &uploadReadback); + + const uint32_t customChannelDataLen = InstallCustomChannelDataLen(instructionBytes, options); + TileXRCcuDriverAdapterReport driverReport; + const int installRet = adapter.InstallInstructionsWithDataLen( + image.dieId, + installStartId, + installCount, + result.deviceInstructionAddr, + static_cast(instructionBytes), + customChannelDataLen, + &driverReport); + if (installRet != TILEXR_SUCCESS) { + TileXRCcuUploadReadbackDiagnostic driverReadback; + BuildDriverInstructionReadbackDiagnostic( + adapter, + image.dieId, + installStartId, + installCount, + instructionBytes, + firstInstruction, + &driverReadback); + (void)memoryOps.free(devicePtr, memoryUserData); + return Fail( + report, + BuildInstallFailureDiagnostic( + driverReport.message, + image, + options, + installOffset, + installStartId, + installCount, + instructionBytes, + customChannelDataLen, + result.deviceInstructionAddr, + uploadReadback, + driverReadback), + TILEXR_ERROR_MKIRT); + } + + result.installed = true; + *receipt = result; + FillInstallReport(*receipt, report); + if (report != nullptr && + (options.window != TileXRCcuRepositoryInstallWindow::Mission || + options.dataLenMode != TileXRCcuRepositoryInstallDataLenMode::InstructionBytes)) { + report->message = std::string("ok window=") + InstallWindowText(options.window) + + " dataLenMode=" + InstallDataLenModeText(options.dataLenMode); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuReleaseRepositoryInstallReceipt( + TileXRCcuRepositoryInstallReceipt& receipt, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + TileXRCcuRepositoryReport* report) +{ + ResetReport(report); + if (memoryOps.free == nullptr) { + return Fail(report, "missing CCU repository device free hook"); + } + if (receipt.deviceInstructionPtr == nullptr) { + receipt = TileXRCcuRepositoryInstallReceipt{}; + if (report != nullptr) { + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + const int freeRet = memoryOps.free(receipt.deviceInstructionPtr, memoryUserData); + if (freeRet != 0) { + return Fail(report, "failed to release CCU repository device instruction image", TILEXR_ERROR_MKIRT); + } + receipt = TileXRCcuRepositoryInstallReceipt{}; + if (report != nullptr) { + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_repository.h b/src/comm/ccu/tilexr_ccu_repository.h new file mode 100644 index 0000000..5a7fdc0 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_repository.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_REPOSITORY_H +#define TILEXR_CCU_REPOSITORY_H + +#include "ccu/tilexr_ccu_driver_adapter.h" +#include "ccu/tilexr_ccu_producer_plan.h" + +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuRepositoryImage { + uint8_t dieId = 0; + uint16_t repositoryStartId = 0; + uint16_t repositoryCount = 0; + uint16_t missionStartId = 0; + uint16_t missionCount = 0; + uint16_t missionOffset = 0; + uint16_t sqeLoadOffset = 0; + uint16_t sqeLoadCount = 0; + uint16_t syncOffset = 0; + uint16_t syncCount = 0; + std::vector instructions; +}; + +struct TileXRCcuRepositoryReport { + uint16_t repositoryCount = 0; + uint16_t installedInstructionCount = 0; + uint16_t sqeLoadOffset = 0; + uint16_t syncOffset = 0; + bool repositoryUploaded = false; + bool repositoryInstalled = false; + std::string message; +}; + +using TileXRCcuDeviceAllocFn = int (*)(uint64_t bytes, void** devicePtr, void* userData); +using TileXRCcuCopyHostToDeviceFn = int (*)( + void* deviceDst, + uint64_t deviceDstBytes, + const void* hostSrc, + uint64_t bytes, + void* userData); +using TileXRCcuCopyDeviceToHostFn = int (*)( + void* hostDst, + uint64_t hostDstBytes, + const void* deviceSrc, + uint64_t bytes, + void* userData); +using TileXRCcuDeviceFreeFn = int (*)(void* devicePtr, void* userData); + +struct TileXRCcuDeviceMemoryOps { + TileXRCcuDeviceAllocFn alloc = nullptr; + TileXRCcuCopyHostToDeviceFn copyHostToDevice = nullptr; + TileXRCcuDeviceFreeFn free = nullptr; + TileXRCcuCopyDeviceToHostFn copyDeviceToHost = nullptr; +}; + +enum class TileXRCcuRepositoryMemoryAllocMode : uint8_t { + Acl = 0, + AclModule3 = 1, + RtHbm = 2, +}; + +struct TileXRCcuRepositoryInstallReceipt { + uint8_t dieId = 0; + uint16_t instructionStartId = 0; + uint16_t instructionCount = 0; + uint64_t instructionBytes = 0; + uint64_t deviceInstructionAddr = 0; + void* deviceInstructionPtr = nullptr; + bool uploaded = false; + bool installed = false; +}; + +enum class TileXRCcuRepositoryInstallWindow : uint8_t { + Mission = 0, + FullRepository = 1, +}; + +enum class TileXRCcuRepositoryInstallDataLenMode : uint8_t { + InstructionBytes = 0, + DescriptorBytes = 1, +}; + +struct TileXRCcuRepositoryInstallOptions { + TileXRCcuRepositoryInstallWindow window = TileXRCcuRepositoryInstallWindow::Mission; + TileXRCcuRepositoryInstallDataLenMode dataLenMode = TileXRCcuRepositoryInstallDataLenMode::InstructionBytes; +}; + +TileXRCcuDeviceMemoryOps TileXRCcuMakeAclDeviceMemoryOps(); +TileXRCcuDeviceMemoryOps TileXRCcuMakeAclModule3DeviceMemoryOps(); +TileXRCcuDeviceMemoryOps TileXRCcuMakeRtHbmDeviceMemoryOps(); +TileXRCcuDeviceMemoryOps TileXRCcuMakeRepositoryDeviceMemoryOps(TileXRCcuRepositoryMemoryAllocMode mode); + +int TileXRCcuBuildRepositoryImage( + const TileXRCcuProducerPlan& plan, + const TileXRCcuProgram& program, + TileXRCcuRepositoryImage* image, + TileXRCcuRepositoryReport* report); + +int TileXRCcuInstallRepositoryImage( + const TileXRCcuRepositoryImage& image, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + const TileXRCcuDriverAdapter& adapter, + TileXRCcuRepositoryInstallReceipt* receipt, + TileXRCcuRepositoryReport* report); + +int TileXRCcuInstallRepositoryImageWithOptions( + const TileXRCcuRepositoryImage& image, + const TileXRCcuRepositoryInstallOptions& options, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + const TileXRCcuDriverAdapter& adapter, + TileXRCcuRepositoryInstallReceipt* receipt, + TileXRCcuRepositoryReport* report); + +int TileXRCcuReleaseRepositoryInstallReceipt( + TileXRCcuRepositoryInstallReceipt& receipt, + const TileXRCcuDeviceMemoryOps& memoryOps, + void* memoryUserData, + TileXRCcuRepositoryReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_REPOSITORY_H diff --git a/src/comm/ccu/tilexr_ccu_resource_allocator.cpp b/src/comm/ccu/tilexr_ccu_resource_allocator.cpp new file mode 100644 index 0000000..e9bb407 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_resource_allocator.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_resource_allocator.h" + +#include +#include + +namespace TileXR { +namespace { + +constexpr const char* TILEXR_CCU_HCOMM_DERIVED_PROVIDER = "tilexr-hcomm-derived-resource-allocator"; +constexpr uint32_t TILEXR_CCU_HCOMM_TASK1_PRELUDE_INSTRUCTION_COUNT = 5U; +constexpr uint32_t TILEXR_CCU_HCOMM_TASK1_PRELUDE_RESERVED_XN_COUNT = 1U; + +void ResetReport(TileXRCcuResourceAllocatorReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuResourceAllocatorReport{}; + } +} + +int Fail(TileXRCcuResourceAllocatorReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +bool AddWouldOverflow(uint16_t start, uint16_t count) +{ + return static_cast(start) + count > static_cast(std::numeric_limits::max()) + 1U; +} + +bool ContainsRange(uint16_t outerStart, uint16_t outerCount, uint16_t innerStart, uint32_t innerCount) +{ + if (outerCount == 0 || innerCount == 0) { + return false; + } + const uint32_t outerBegin = outerStart; + const uint32_t outerEnd = outerBegin + outerCount; + const uint32_t innerBegin = innerStart; + const uint32_t innerEnd = innerBegin + innerCount; + return innerBegin >= outerBegin && innerEnd <= outerEnd; +} + +TileXRCcuRange MakeRange(uint8_t dieId, uint16_t startId, uint16_t count) +{ + TileXRCcuRange range; + range.dieId = dieId; + range.startId = startId; + range.num = count; + return range; +} + +void FillReport( + const TileXRCcuResourceAllocation& allocation, + const TileXRCcuResourceRequest& request, + TileXRCcuResourceAllocatorReport* report) +{ + if (report == nullptr) { + return; + } + report->missionAllocated = allocation.mission.num; + report->repositoryAllocated = allocation.repository.num; + report->localXnAllocated = allocation.localXn.num; + report->localGsaAllocated = allocation.localGsa.num; + report->remoteXnAllocated = allocation.remoteXn.num; + report->notifyCkeAllocated = allocation.notifyCke.num; + report->channelBindingsAllocated = request.syncResourceCount * request.bindingsPerSyncResource; + report->localWaitCkeAllocated = allocation.localWaitCke.num; + report->remoteNotifyCkeAllocated = allocation.remoteNotifyCke.num; + report->sourceCkeAllocated = allocation.sourceCke.num; + report->message = "ok"; +} + +uint16_t CheckedU16(uint32_t value) +{ + return static_cast(std::min(value, std::numeric_limits::max())); +} + +bool SyncXnMode(TileXRCcuBarrierMode mode) +{ + return mode == TileXRCcuBarrierMode::SyncXn || + mode == TileXRCcuBarrierMode::SyncXnPostOnly || + mode == TileXRCcuBarrierMode::SyncXnLoadPostOnly; +} + +uint32_t RequiredSqeLoadXnCount(uint32_t sqeArgCount, bool hcommStyleTask1Prelude) +{ + if (sqeArgCount == 0) { + return 0; + } + + uint32_t count = hcommStyleTask1Prelude ? + std::max(sqeArgCount, TILEXR_CCU_SQE_ARGS_LEN) + + TILEXR_CCU_HCOMM_TASK1_PRELUDE_RESERVED_XN_COUNT : + sqeArgCount; + return count; +} + +} // namespace + +bool TileXRCcuResourceAllocator::HasCapacity(const Cursor& cursor, uint32_t count) const +{ + return count <= std::numeric_limits::max() && + static_cast(cursor.used) + count <= cursor.count; +} + +uint16_t TileXRCcuResourceAllocator::CursorNext(const Cursor& cursor) const +{ + return static_cast(cursor.start + cursor.used); +} + +int TileXRCcuResourceAllocator::Init(const TileXRCcuResourceSpec& spec) +{ + const uint16_t missionInstructionStart = + spec.missionInstructionStartId == 0 ? spec.instructionStartId : spec.missionInstructionStartId; + const uint16_t localWaitCkeStart = + spec.localWaitCkeCount == 0 ? spec.ckeStartId : spec.localWaitCkeStartId; + const uint16_t localWaitCkeCount = + spec.localWaitCkeCount == 0 ? spec.ckeCount : spec.localWaitCkeCount; + const uint16_t remoteNotifyCkeStart = + spec.remoteNotifyCkeCount == 0 ? spec.ckeStartId : spec.remoteNotifyCkeStartId; + const uint16_t remoteNotifyCkeCount = + spec.remoteNotifyCkeCount == 0 ? spec.ckeCount : spec.remoteNotifyCkeCount; + const bool splitRemoteXn = spec.remoteXnCount != 0; + + if (spec.missionKey == 0 || spec.missionCount == 0 || spec.instructionCount == 0 || + spec.xnCount == 0 || localWaitCkeCount == 0 || remoteNotifyCkeCount == 0 || + spec.channelCount == 0 || + AddWouldOverflow(spec.missionStartId, spec.missionCount) || + AddWouldOverflow(spec.instructionStartId, spec.instructionCount) || + !ContainsRange(spec.instructionStartId, spec.instructionCount, missionInstructionStart, 1) || + AddWouldOverflow(spec.xnStartId, spec.xnCount) || + (spec.gsaCount != 0 && (spec.gsaStartId == 0 || AddWouldOverflow(spec.gsaStartId, spec.gsaCount))) || + (splitRemoteXn && (spec.remoteXnStartId == 0 || AddWouldOverflow(spec.remoteXnStartId, spec.remoteXnCount))) || + AddWouldOverflow(localWaitCkeStart, localWaitCkeCount) || + AddWouldOverflow(remoteNotifyCkeStart, remoteNotifyCkeCount) || + AddWouldOverflow(spec.channelStartId, spec.channelCount)) { + initialized_ = false; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + spec_ = spec; + mission_ = {spec.missionStartId, spec.missionCount, 0}; + repository_ = {spec.instructionStartId, spec.instructionCount, 0}; + xn_ = {spec.xnStartId, spec.xnCount, 0}; + gsa_ = {spec.gsaStartId, spec.gsaCount, 0}; + remoteXn_ = splitRemoteXn ? Cursor{spec.remoteXnStartId, spec.remoteXnCount, 0} : Cursor{}; + localWaitCke_ = {localWaitCkeStart, localWaitCkeCount, 0}; + remoteNotifyCke_ = {remoteNotifyCkeStart, remoteNotifyCkeCount, 0}; + channel_ = {spec.channelStartId, spec.channelCount, 0}; + nextReceiptId_ = 1; + active_.clear(); + initialized_ = true; + return TILEXR_SUCCESS; +} + +int TileXRCcuResourceAllocator::Allocate( + const TileXRCcuResourceRequest& request, + TileXRCcuProducerPlan* plan, + TileXRCcuResourceAllocation* allocation, + TileXRCcuResourceAllocatorReport* report) +{ + ResetReport(report); + if (!initialized_) { + return Fail(report, "CCU resource allocator is not initialized"); + } + if (plan == nullptr || allocation == nullptr) { + return Fail(report, "missing CCU resource allocation output"); + } + *plan = TileXRCcuProducerPlan{}; + *allocation = TileXRCcuResourceAllocation{}; + + if (request.sqeArgCount > TILEXR_CCU_SQE_ARGS_LEN) { + return Fail(report, "invalid SQE argument count"); + } + if (request.syncResourceCount == 0 || request.syncInstructionCount == 0 || + request.bindingsPerSyncResource == 0) { + return Fail(report, "invalid CCU sync resource request"); + } + if (request.syncResourceCount > std::numeric_limits::max() / 2U) { + return Fail(report, "barrier sync instruction window is too small for CCU post and wait microcode"); + } + const bool syncCkeMode = request.barrierMode == TileXRCcuBarrierMode::SyncCke || + request.barrierMode == TileXRCcuBarrierMode::SyncCkeSetWait || + request.barrierMode == TileXRCcuBarrierMode::SyncCkePostOnly; + const bool hcommStyleTask1Prelude = request.sqeArgCount != 0 && SyncXnMode(request.barrierMode); + const bool postOnly = request.barrierMode == TileXRCcuBarrierMode::SyncXnPostOnly || + request.barrierMode == TileXRCcuBarrierMode::SyncCkePostOnly || + request.barrierMode == TileXRCcuBarrierMode::LocalCkePostOnly; + const uint32_t requiredPostWaitInstructionCount = + postOnly ? request.syncResourceCount : request.syncResourceCount * 2U; + const uint32_t sourceCkeInitCount = syncCkeMode ? 1U : 0U; + const uint32_t sourceCkeResourceCount = syncCkeMode ? 1U : 0U; + const uint32_t task1PreludeInstructionCount = + hcommStyleTask1Prelude ? TILEXR_CCU_HCOMM_TASK1_PRELUDE_INSTRUCTION_COUNT : 0U; + const uint32_t requiredBarrierInstructionCount = + requiredPostWaitInstructionCount + sourceCkeInitCount + task1PreludeInstructionCount; + if (request.syncInstructionCount < requiredBarrierInstructionCount) { + return Fail(report, + hcommStyleTask1Prelude ? + "barrier sync instruction window is too small for hcomm-style task1 prelude and CCU post/wait microcode" : + "barrier sync instruction window is too small for CCU post and wait microcode"); + } + + const uint32_t localSqeXnCount = RequiredSqeLoadXnCount(request.sqeArgCount, hcommStyleTask1Prelude); + const uint32_t localXnCount = std::max(localSqeXnCount, request.syncResourceCount); + const uint32_t remoteXnCount = request.syncResourceCount; + const uint32_t localGsaCount = hcommStyleTask1Prelude && spec_.gsaCount != 0 ? 1U : 0U; + const uint32_t totalXnCount = localXnCount + remoteXnCount; + const uint32_t localWaitCkeCount = request.syncResourceCount; + const uint32_t remoteNotifyCkeCount = request.syncResourceCount; + const uint32_t localCkeCount = localWaitCkeCount + sourceCkeResourceCount; + const uint16_t repositoryStart = CursorNext(repository_); + const uint16_t missionInstructionStart = + spec_.missionInstructionStartId == 0 ? repositoryStart : spec_.missionInstructionStartId; + if (!ContainsRange(repositoryStart, repository_.count, missionInstructionStart, 1)) { + return Fail(report, "mission instruction start is outside instruction repository resources"); + } + const uint32_t repositoryPrefixCount = + static_cast(missionInstructionStart) - static_cast(repositoryStart); + const uint32_t missionInstructionCount = request.sqeArgCount + request.syncInstructionCount; + const uint32_t repositoryCount = repositoryPrefixCount + missionInstructionCount; + const uint32_t channelCount = request.syncResourceCount; + + if (!HasCapacity(mission_, 1)) { + return Fail(report, "insufficient mission resources"); + } + if (!HasCapacity(repository_, repositoryCount)) { + return Fail(report, "insufficient instruction repository resources"); + } + const bool splitRemoteXn = remoteXn_.count != 0; + if (!HasCapacity(xn_, splitRemoteXn ? localXnCount : totalXnCount)) { + return Fail(report, "insufficient XN resources"); + } + if (localGsaCount != 0 && !HasCapacity(gsa_, localGsaCount)) { + return Fail(report, "insufficient GSA resources"); + } + if (splitRemoteXn && !HasCapacity(remoteXn_, remoteXnCount)) { + return Fail(report, "insufficient remote XN resources"); + } + if (!HasCapacity(localWaitCke_, localCkeCount)) { + return Fail(report, "insufficient CKE resources"); + } + if (!HasCapacity(remoteNotifyCke_, remoteNotifyCkeCount)) { + return Fail(report, "insufficient remote notify CKE resources"); + } + if (!HasCapacity(channel_, channelCount)) { + return Fail(report, "insufficient channel resources"); + } + + const uint16_t missionStart = CursorNext(mission_); + const uint16_t localXnStart = CursorNext(xn_); + const uint16_t localGsaStart = CursorNext(gsa_); + const uint16_t remoteXnStart = splitRemoteXn ? + CursorNext(remoteXn_) : + static_cast(localXnStart + localXnCount); + const uint16_t localWaitCkeStart = CursorNext(localWaitCke_); + const uint16_t sourceCkeStart = static_cast(localWaitCkeStart + localWaitCkeCount); + const uint16_t remoteNotifyCkeStart = CursorNext(remoteNotifyCke_); + const uint16_t channelStart = CursorNext(channel_); + + TileXRCcuResourceAllocation result; + result.receiptId = nextReceiptId_++; + result.packageProvider = TILEXR_CCU_HCOMM_DERIVED_PROVIDER; + result.mission = MakeRange(spec_.dieId, missionStart, 1); + result.repository = MakeRange(spec_.dieId, repositoryStart, CheckedU16(repositoryCount)); + result.localXn = MakeRange(spec_.dieId, localXnStart, CheckedU16(localXnCount)); + result.localGsa = MakeRange(spec_.dieId, localGsaStart, CheckedU16(localGsaCount)); + result.remoteXn = MakeRange(spec_.dieId, remoteXnStart, CheckedU16(remoteXnCount)); + result.notifyCke = MakeRange(spec_.dieId, remoteNotifyCkeStart, CheckedU16(remoteNotifyCkeCount)); + result.channels = MakeRange(spec_.dieId, channelStart, CheckedU16(channelCount)); + result.localWaitCke = MakeRange(spec_.dieId, localWaitCkeStart, CheckedU16(localWaitCkeCount)); + result.remoteNotifyCke = result.notifyCke; + result.sourceCke = MakeRange(spec_.dieId, sourceCkeStart, CheckedU16(sourceCkeResourceCount)); + + TileXRCcuProducerPlan generated; + generated.barrierMode = request.barrierMode; + generated.mission = {spec_.dieId, static_cast(missionStart), spec_.missionKey, true}; + generated.kernelLocalMission = result.mission; + generated.kernelLocalXn = result.localXn; + generated.kernelLocalGsa = result.localGsa; + generated.kernelLocalCke = MakeRange(spec_.dieId, localWaitCkeStart, CheckedU16(localCkeCount)); + generated.instructionWindow = { + spec_.dieId, + result.repository.startId, + result.repository.num, + missionInstructionStart, + CheckedU16(missionInstructionCount), + }; + + for (uint32_t i = 0; i < request.syncResourceCount; ++i) { + TileXRCcuSyncResource resource; + resource.dieId = spec_.dieId; + resource.localXn = static_cast(result.localXn.startId + i); + resource.remoteXn = static_cast(static_cast(result.remoteXn.startId) + i); + resource.notifyCke = static_cast(static_cast(result.remoteNotifyCke.startId) + i); + resource.channelId = static_cast(static_cast(result.channels.startId) + i); + resource.bindingCount = CheckedU16(request.bindingsPerSyncResource); + resource.localWaitCke = static_cast(result.localWaitCke.startId + i); + resource.localWaitMask = 1; + resource.remoteNotifyMask = 1; + if (syncCkeMode) { + resource.sourceCke = result.sourceCke.startId; + resource.sourceCkeMask = 0xffff; + } + generated.syncResources.push_back(resource); + } + + if (request.sqeArgCount != 0) { + TileXRCcuTaskWindow sqeLoadTask; + sqeLoadTask.dieId = spec_.dieId; + sqeLoadTask.instStartId = missionInstructionStart; + sqeLoadTask.instCnt = CheckedU16(request.sqeArgCount); + sqeLoadTask.argSize = TILEXR_CCU_SQE_ARGS_LEN; + generated.taskWindows.push_back(sqeLoadTask); + } + + TileXRCcuTaskWindow syncTask; + syncTask.dieId = spec_.dieId; + syncTask.instStartId = static_cast(missionInstructionStart + request.sqeArgCount); + syncTask.instCnt = CheckedU16(request.syncInstructionCount); + syncTask.argSize = TILEXR_CCU_SQE_ARGS_LEN; + generated.taskWindows.push_back(syncTask); + + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(generated, &planReport) != TILEXR_SUCCESS) { + return Fail(report, planReport.message); + } + + mission_.used = static_cast(mission_.used + result.mission.num); + repository_.used = static_cast(repository_.used + result.repository.num); + xn_.used = static_cast(xn_.used + result.localXn.num + (splitRemoteXn ? 0 : result.remoteXn.num)); + gsa_.used = static_cast(gsa_.used + result.localGsa.num); + if (splitRemoteXn) { + remoteXn_.used = static_cast(remoteXn_.used + result.remoteXn.num); + } + localWaitCke_.used = static_cast(localWaitCke_.used + result.localWaitCke.num + result.sourceCke.num); + remoteNotifyCke_.used = static_cast(remoteNotifyCke_.used + result.remoteNotifyCke.num); + channel_.used = static_cast(channel_.used + result.channels.num); + + ActiveAllocation active; + active.allocation = result; + active.missionUsed = result.mission.num; + active.repositoryUsed = result.repository.num; + active.localXnUsed = result.localXn.num; + active.localGsaUsed = result.localGsa.num; + active.remoteXnUsed = result.remoteXn.num; + active.notifyCkeUsed = result.notifyCke.num; + active.channelUsed = result.channels.num; + active.localWaitCkeUsed = result.localWaitCke.num; + active.remoteNotifyCkeUsed = result.remoteNotifyCke.num; + active.sourceCkeUsed = result.sourceCke.num; + active_[result.receiptId] = active; + + *plan = generated; + *allocation = result; + FillReport(result, request, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuResourceAllocator::Release(uint64_t receiptId) +{ + const auto it = active_.find(receiptId); + if (it == active_.end()) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + active_.erase(it); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_resource_allocator.h b/src/comm/ccu/tilexr_ccu_resource_allocator.h new file mode 100644 index 0000000..706ff2b --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_resource_allocator.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_RESOURCE_ALLOCATOR_H +#define TILEXR_CCU_RESOURCE_ALLOCATOR_H + +#include "ccu/tilexr_ccu_producer_plan.h" + +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuResourceSpec { + uint8_t dieId = 0; + uint32_t missionKey = 0; + uint16_t missionStartId = 0; + uint16_t missionCount = 0; + uint16_t instructionStartId = 0; + uint16_t missionInstructionStartId = 0; + uint16_t instructionCount = 0; + uint16_t xnStartId = 0; + uint16_t xnCount = 0; + uint16_t gsaStartId = 0; + uint16_t gsaCount = 0; + uint16_t remoteXnStartId = 0; + uint16_t remoteXnCount = 0; + uint16_t ckeStartId = 0; + uint16_t ckeCount = 0; + uint16_t channelStartId = 0; + uint16_t channelCount = 0; + uint16_t localWaitCkeStartId = 0; + uint16_t localWaitCkeCount = 0; + uint16_t remoteNotifyCkeStartId = 0; + uint16_t remoteNotifyCkeCount = 0; +}; + +struct TileXRCcuResourceRequest { + uint32_t sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + uint32_t syncResourceCount = 0; + uint32_t syncInstructionCount = 0; + uint32_t bindingsPerSyncResource = 1; + TileXRCcuBarrierMode barrierMode = TileXRCcuBarrierMode::SyncXn; +}; + +struct TileXRCcuResourceAllocation { + uint64_t receiptId = 0; + std::string packageProvider; + TileXRCcuRange mission; + TileXRCcuRange repository; + TileXRCcuRange localXn; + TileXRCcuRange localGsa; + TileXRCcuRange remoteXn; + TileXRCcuRange notifyCke; + TileXRCcuRange channels; + TileXRCcuRange localWaitCke; + TileXRCcuRange remoteNotifyCke; + TileXRCcuRange sourceCke; +}; + +struct TileXRCcuResourceAllocatorReport { + uint32_t missionAllocated = 0; + uint32_t repositoryAllocated = 0; + uint32_t localXnAllocated = 0; + uint32_t localGsaAllocated = 0; + uint32_t remoteXnAllocated = 0; + uint32_t notifyCkeAllocated = 0; + uint32_t channelBindingsAllocated = 0; + uint32_t localWaitCkeAllocated = 0; + uint32_t remoteNotifyCkeAllocated = 0; + uint32_t sourceCkeAllocated = 0; + std::string message; +}; + +class TileXRCcuResourceAllocator { +public: + int Init(const TileXRCcuResourceSpec& spec); + + int Allocate( + const TileXRCcuResourceRequest& request, + TileXRCcuProducerPlan* plan, + TileXRCcuResourceAllocation* allocation, + TileXRCcuResourceAllocatorReport* report); + + int Release(uint64_t receiptId); + +private: + struct Cursor { + uint16_t start = 0; + uint16_t count = 0; + uint16_t used = 0; + }; + + bool HasCapacity(const Cursor& cursor, uint32_t count) const; + uint16_t CursorNext(const Cursor& cursor) const; + + struct ActiveAllocation { + TileXRCcuResourceAllocation allocation; + uint16_t missionUsed = 0; + uint16_t repositoryUsed = 0; + uint16_t localXnUsed = 0; + uint16_t localGsaUsed = 0; + uint16_t remoteXnUsed = 0; + uint16_t notifyCkeUsed = 0; + uint16_t channelUsed = 0; + uint16_t localWaitCkeUsed = 0; + uint16_t remoteNotifyCkeUsed = 0; + uint16_t sourceCkeUsed = 0; + }; + + TileXRCcuResourceSpec spec_; + Cursor mission_; + Cursor repository_; + Cursor xn_; + Cursor gsa_; + Cursor remoteXn_; + Cursor localWaitCke_; + Cursor remoteNotifyCke_; + Cursor channel_; + uint64_t nextReceiptId_ = 1; + bool initialized_ = false; + std::map active_; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_RESOURCE_ALLOCATOR_H diff --git a/src/comm/ccu/tilexr_ccu_runtime.cpp b/src/comm/ccu/tilexr_ccu_runtime.cpp new file mode 100644 index 0000000..138b7cb --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_runtime.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_runtime.h" + +#include + +#include + +namespace TileXR { + +static_assert(RT_CCU_SQE_ARGS_LEN == TILEXR_CCU_SQE_ARGS_LEN, "TileXR CCU SQE arg count must match CANN runtime"); + +namespace { + +TileXRCcuTask CopyRuntimeTask(const rtCcuTaskInfo_t& runtimeTask) +{ + TileXRCcuTask task {}; + task.dieId = runtimeTask.dieId; + task.missionId = runtimeTask.missionId; + task.timeout = runtimeTask.timeout; + task.instStartId = runtimeTask.instStartId; + task.instCnt = runtimeTask.instCnt; + task.key = runtimeTask.key; + task.argSize = runtimeTask.argSize; + std::memcpy(task.args, runtimeTask.args, sizeof(task.args)); + return task; +} + +} // namespace + +int TileXRCcuValidateTask(const TileXRCcuTask& task) +{ + if (task.instCnt == RT_CCU_INST_CNT_INVALID) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (task.instStartId >= RT_CCU_INST_START_MAX) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (static_cast(task.instStartId) + static_cast(task.instCnt) > RT_CCU_INST_START_MAX) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (task.argSize != 1 && task.argSize != TILEXR_CCU_SQE_ARGS_LEN) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuSubmitTaskWithReport( + const TileXRCcuTask& task, + void* stream, + TileXRCcuRuntimeSubmitReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuRuntimeSubmitReport{}; + } + if (stream == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + int ret = TileXRCcuValidateTask(task); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + rtCcuTaskInfo_t runtimeTask {}; + runtimeTask.dieId = task.dieId; + runtimeTask.missionId = task.missionId; + runtimeTask.timeout = task.timeout; + runtimeTask.instStartId = task.instStartId; + runtimeTask.instCnt = task.instCnt; + runtimeTask.key = task.key; + runtimeTask.argSize = task.argSize; + std::memcpy(runtimeTask.args, task.args, sizeof(runtimeTask.args)); + + const TileXRCcuTask finalTask = CopyRuntimeTask(runtimeTask); + if (report != nullptr) { + report->finalTaskCaptured = true; + report->finalTask = finalTask; + } + + rtError_t launchRet = rtCCULaunch(&runtimeTask, stream); + if (report != nullptr) { + report->runtimeLaunchAttempted = true; + report->runtimeRet = static_cast(launchRet); + } + return launchRet == RT_ERROR_NONE ? TILEXR_SUCCESS : TILEXR_ERROR_MKIRT; +} + +int TileXRCcuSubmitTask(const TileXRCcuTask& task, void* stream) +{ + return TileXRCcuSubmitTaskWithReport(task, stream, nullptr); +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_runtime.h b/src/comm/ccu/tilexr_ccu_runtime.h new file mode 100644 index 0000000..7d40699 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_runtime.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_RUNTIME_H +#define TILEXR_CCU_RUNTIME_H + +#include + +#include "tilexr_types.h" + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_SQE_ARGS_LEN = 13U; +constexpr uint16_t TILEXR_CCU_DEFAULT_TASK_TIMEOUT_SEC = 120U; + +struct TileXRCcuTask { + uint8_t dieId = 0; + uint8_t missionId = 0; + uint16_t timeout = 0; + uint16_t instStartId = 0; + uint16_t instCnt = 0; + uint32_t key = 0; + uint32_t argSize = 0; + uint64_t args[TILEXR_CCU_SQE_ARGS_LEN] = {}; +}; + +struct TileXRCcuRuntimeSubmitReport { + bool runtimeLaunchAttempted = false; + int32_t runtimeRet = 0; + bool finalTaskCaptured = false; + TileXRCcuTask finalTask; +}; + +int TileXRCcuValidateTask(const TileXRCcuTask& task); + +int TileXRCcuSubmitTaskWithReport( + const TileXRCcuTask& task, + void* stream, + TileXRCcuRuntimeSubmitReport* report); + +int TileXRCcuSubmitTask(const TileXRCcuTask& task, void* stream); + +} // namespace TileXR + +#endif // TILEXR_CCU_RUNTIME_H diff --git a/src/comm/ccu/tilexr_ccu_specs.cpp b/src/comm/ccu/tilexr_ccu_specs.cpp new file mode 100644 index 0000000..06cdcde --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_specs.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_specs.h" + +#include + +namespace TileXR { +namespace { + +constexpr uint32_t TILEXR_CCU_MOVE_16_BITS = 16; +constexpr uint32_t TILEXR_CCU_MOVE_24_BITS = 24; + +void ResetReport(TileXRCcuSpecsReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRCcuSpecsReport{}; +} + +int Fail(TileXRCcuSpecsReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +uint32_t Low16PlusOne(uint32_t value) +{ + return (value & 0x0000FFFFU) + 1U; +} + +uint32_t High16PlusOne(uint32_t value) +{ + return ((value >> TILEXR_CCU_MOVE_16_BITS) & 0x0000FFFFU) + 1U; +} + +bool FitsU16(uint32_t value) +{ + return value <= std::numeric_limits::max(); +} + +bool WindowOverflows(uint16_t start, uint32_t count) +{ + return count == 0 || count > std::numeric_limits::max() || + static_cast(start) + count > + static_cast(std::numeric_limits::max()) + 1U; +} + +void FillReport(const TileXRCcuSpecInfo& info, TileXRCcuSpecsReport* report) +{ + if (report == nullptr) { + return; + } + report->instructionNum = info.instructionNum; + report->xnNum = info.xnNum; + report->ckeNum = info.ckeNum; + report->channelNum = info.channelNum; + report->missionNum = info.missionNum; + report->message = "ok"; +} + +} // namespace + +int TileXRCcuDecodeBasicInfo( + const TileXRCcuBasicInfo& basicInfo, + TileXRCcuSpecInfo* specInfo, + TileXRCcuSpecsReport* report) +{ + ResetReport(report); + if (specInfo == nullptr) { + return Fail(report, "missing output CCU spec info"); + } + *specInfo = TileXRCcuSpecInfo{}; + + if (basicInfo.missionKey == 0) { + return Fail(report, "missing CCU mission key in basic info"); + } + if (basicInfo.resourceAddr == 0) { + return Fail(report, "missing CCU resource address in basic info"); + } + if (basicInfo.resourceAddr > std::numeric_limits::max() - TILEXR_CCU_V1_XN_RESOURCE_OFFSET) { + return Fail(report, "CCU XN base address overflows"); + } + + TileXRCcuSpecInfo decoded; + decoded.dieId = basicInfo.dieId; + decoded.msId = basicInfo.msId; + decoded.missionKey = basicInfo.missionKey; + decoded.resourceAddr = basicInfo.resourceAddr; + decoded.xnBaseAddr = basicInfo.resourceAddr + TILEXR_CCU_V1_XN_RESOURCE_OFFSET; + + decoded.instructionNum = Low16PlusOne(basicInfo.caps.cap0); + decoded.xnNum = High16PlusOne(basicInfo.caps.cap1); + decoded.gsaNum = Low16PlusOne(basicInfo.caps.cap1); + decoded.msNum = High16PlusOne(basicInfo.caps.cap2); + decoded.ckeNum = Low16PlusOne(basicInfo.caps.cap2); + decoded.jettyNum = High16PlusOne(basicInfo.caps.cap3); + decoded.channelNum = Low16PlusOne(basicInfo.caps.cap3); + decoded.pfeNum = (basicInfo.caps.cap4 & 0x000000FFU) + 1U; + decoded.missionNum = ((basicInfo.caps.cap0 >> TILEXR_CCU_MOVE_16_BITS) & 0x000000FFU) + 1U; + decoded.loopEngineNum = ((basicInfo.caps.cap0 >> TILEXR_CCU_MOVE_24_BITS) & 0x000000FFU) + 1U; + + *specInfo = decoded; + FillReport(decoded, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildResourceSpec( + const TileXRCcuSpecInfo& specInfo, + uint16_t missionStartId, + uint16_t instructionStartId, + uint16_t xnStartId, + uint16_t ckeStartId, + uint16_t channelStartId, + TileXRCcuResourceSpec* resourceSpec, + TileXRCcuSpecsReport* report, + uint16_t gsaStartId) +{ + ResetReport(report); + if (resourceSpec == nullptr) { + return Fail(report, "missing output CCU resource spec"); + } + *resourceSpec = TileXRCcuResourceSpec{}; + + if (specInfo.missionKey == 0) { + return Fail(report, "missing CCU mission key in spec info"); + } + if (!FitsU16(specInfo.missionNum) || !FitsU16(specInfo.instructionNum) || !FitsU16(specInfo.xnNum) || + !FitsU16(specInfo.gsaNum) || + !FitsU16(specInfo.ckeNum) || !FitsU16(specInfo.channelNum)) { + return Fail(report, "CCU resource count exceeds TileXR resource window capacity"); + } + if (WindowOverflows(missionStartId, specInfo.missionNum)) { + return Fail(report, "mission resource window overflows"); + } + if (WindowOverflows(instructionStartId, specInfo.instructionNum)) { + return Fail(report, "instruction resource window overflows"); + } + if (WindowOverflows(xnStartId, specInfo.xnNum)) { + return Fail(report, "XN resource window overflows"); + } + if (gsaStartId != 0 && WindowOverflows(gsaStartId, specInfo.gsaNum)) { + return Fail(report, "GSA resource window overflows"); + } + if (WindowOverflows(ckeStartId, specInfo.ckeNum)) { + return Fail(report, "CKE resource window overflows"); + } + if (WindowOverflows(channelStartId, specInfo.channelNum)) { + return Fail(report, "channel resource window overflows"); + } + + TileXRCcuResourceSpec result; + result.dieId = specInfo.dieId; + result.missionKey = specInfo.missionKey; + result.missionStartId = missionStartId; + result.missionCount = static_cast(specInfo.missionNum); + result.instructionStartId = instructionStartId; + result.instructionCount = static_cast(specInfo.instructionNum); + result.xnStartId = xnStartId; + result.xnCount = static_cast(specInfo.xnNum); + result.gsaStartId = gsaStartId; + result.gsaCount = gsaStartId == 0 ? 0 : static_cast(specInfo.gsaNum); + result.ckeStartId = ckeStartId; + result.ckeCount = static_cast(specInfo.ckeNum); + result.channelStartId = channelStartId; + result.channelCount = static_cast(specInfo.channelNum); + + *resourceSpec = result; + FillReport(specInfo, report); + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_specs.h b/src/comm/ccu/tilexr_ccu_specs.h new file mode 100644 index 0000000..75674e4 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_specs.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_SPECS_H +#define TILEXR_CCU_SPECS_H + +#include "ccu/tilexr_ccu_resource_allocator.h" + +#include +#include + +namespace TileXR { + +constexpr uint64_t TILEXR_CCU_V1_CCUM_OFFSET = 0x800000ULL; +constexpr uint64_t TILEXR_CCU_RESOURCE_INS_RESERVE_SIZE = 0x100000ULL; +constexpr uint64_t TILEXR_CCU_V1_RESOURCE_GSA_RESERVE_SIZE = 0x8000ULL; +constexpr uint64_t TILEXR_CCU_V1_XN_RESOURCE_OFFSET = + TILEXR_CCU_V1_CCUM_OFFSET + TILEXR_CCU_RESOURCE_INS_RESERVE_SIZE + TILEXR_CCU_V1_RESOURCE_GSA_RESERVE_SIZE; +constexpr uint64_t TILEXR_CCU_RESOURCE_WINDOW_BYTES = 72ULL * 1024ULL * 1024ULL; + +struct TileXRCcuCaps { + uint32_t cap0 = 0; + uint32_t cap1 = 0; + uint32_t cap2 = 0; + uint32_t cap3 = 0; + uint32_t cap4 = 0; +}; + +struct TileXRCcuMsidTokenInfo { + uint32_t tokenId = 0; + uint32_t tokenValue = 0; + bool valid = false; +}; + +struct TileXRCcuBasicInfo { + uint8_t dieId = 0; + uint32_t msId = 0; + TileXRCcuMsidTokenInfo msidToken; + uint32_t missionKey = 0; + uint64_t resourceAddr = 0; + TileXRCcuCaps caps; +}; + +struct TileXRCcuSpecInfo { + uint8_t dieId = 0; + uint32_t msId = 0; + uint32_t missionKey = 0; + uint64_t resourceAddr = 0; + uint64_t xnBaseAddr = 0; + uint32_t loopEngineNum = 0; + uint32_t missionNum = 0; + uint32_t instructionNum = 0; + uint32_t xnNum = 0; + uint32_t gsaNum = 0; + uint32_t msNum = 0; + uint32_t ckeNum = 0; + uint32_t jettyNum = 0; + uint32_t channelNum = 0; + uint32_t pfeNum = 0; +}; + +struct TileXRCcuSpecsReport { + uint32_t instructionNum = 0; + uint32_t xnNum = 0; + uint32_t ckeNum = 0; + uint32_t channelNum = 0; + uint32_t missionNum = 0; + std::string message; +}; + +int TileXRCcuDecodeBasicInfo( + const TileXRCcuBasicInfo& basicInfo, + TileXRCcuSpecInfo* specInfo, + TileXRCcuSpecsReport* report); + +int TileXRCcuBuildResourceSpec( + const TileXRCcuSpecInfo& specInfo, + uint16_t missionStartId, + uint16_t instructionStartId, + uint16_t xnStartId, + uint16_t ckeStartId, + uint16_t channelStartId, + TileXRCcuResourceSpec* resourceSpec, + TileXRCcuSpecsReport* report, + uint16_t gsaStartId = 0); + +} // namespace TileXR + +#endif // TILEXR_CCU_SPECS_H diff --git a/src/comm/comm_wrap.cpp b/src/comm/comm_wrap.cpp index 27b1871..4eb717d 100644 --- a/src/comm/comm_wrap.cpp +++ b/src/comm/comm_wrap.cpp @@ -11,7 +11,10 @@ #include #include #include +#include #include +#include +#include #include #include "tilexr_comm.h" @@ -21,6 +24,180 @@ using namespace std; using namespace TileXR; +namespace { + +constexpr const char* TILEXR_DIRECT_CCU_PUBLIC_PROVIDER = "tilexr-public-direct-ccu"; +constexpr const char* TILEXR_DIRECT_CCU_BARRIER_MODE_ENV = "TILEXR_CCU_DIRECT_BARRIER_MODE"; +constexpr uint32_t TILEXR_DIRECT_CCU_MEMORY_COPY_INSTRUCTION_COUNT = 7U; + +struct TileXRDirectCcuPreparedTasks { + ~TileXRDirectCcuPreparedTasks() + { + (void)TileXRCcuReleaseDirectInstallAttemptResources(attempt); + } + + TileXRCcuDirectInstallAttempt attempt; +}; + +void CopyDirectCcuMessage(const std::string& message, char* output) +{ + if (output == nullptr) { + return; + } + std::memset(output, 0, TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES); + if (message.empty()) { + return; + } + std::strncpy(output, message.c_str(), TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES - 1); +} + +void FillPublicPrepareReport( + const TileXRCcuDirectInstallReport& source, + TileXRDirectCcuPrepareReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRDirectCcuPrepareReport {}; + report->pipelineBuilt = source.pipelineBuilt; + report->installAttempted = source.installAttempted; + report->installSucceeded = source.installSucceeded; + report->submitReady = source.submitReady; + report->requiredInstallSurfaceCount = source.requiredInstallSurfaceCount; + report->publicVerifiedInstallSurfaceCount = source.publicVerifiedInstallSurfaceCount; + report->missingInstallSurfaceCount = source.missingInstallSurfaceCount; + report->taskCount = source.taskCount; + report->submitTaskCount = source.submitTaskCount; + CopyDirectCcuMessage(source.message, report->message); +} + +void FillPublicSubmitReport( + const TileXRCcuDirectSubmitReport& source, + TileXRDirectCcuSubmitReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRDirectCcuSubmitReport {}; + report->submitted = source.submitted; + report->taskCount = source.taskCount; + report->submittedTaskCount = source.submittedTaskCount; + CopyDirectCcuMessage(source.message, report->message); +} + +void FillPublicInstructionReadbackReport( + uint32_t readInstructionCount, + const TileXRCcuDriverAdapterReport& source, + TileXRDirectCcuInstructionReadbackReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRDirectCcuInstructionReadbackReport {}; + report->readbackAttempted = true; + report->readInstructionCount = readInstructionCount; + report->opcode = source.opcode; + report->driverRet = source.driverRet; + report->opRet = source.opRet; + CopyDirectCcuMessage(source.message, report->message); +} + +TileXRCcuRepositoryInstallWindow RepositoryInstallWindowFromPublic(uint32_t value) +{ + return value == TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_FULL_REPOSITORY ? + TileXRCcuRepositoryInstallWindow::FullRepository : + TileXRCcuRepositoryInstallWindow::Mission; +} + +TileXRCcuRepositoryInstallDataLenMode RepositoryInstallDataLenModeFromPublic(uint32_t value) +{ + return value == TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_DESCRIPTOR_BYTES ? + TileXRCcuRepositoryInstallDataLenMode::DescriptorBytes : + TileXRCcuRepositoryInstallDataLenMode::InstructionBytes; +} + +TileXRCcuRepositoryMemoryAllocMode RepositoryMemoryAllocModeFromPublic(uint32_t value) +{ + if (value == TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_RT_HBM) { + return TileXRCcuRepositoryMemoryAllocMode::RtHbm; + } + return value == TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL_MODULE3 ? + TileXRCcuRepositoryMemoryAllocMode::AclModule3 : + TileXRCcuRepositoryMemoryAllocMode::Acl; +} + +TileXRCcuInstallOrder InstallOrderFromPublic(uint32_t value) +{ + return value == TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST ? + TileXRCcuInstallOrder::InstallLowerLayerFirst : + TileXRCcuInstallOrder::RepositoryFirst; +} + +TileXRCcuDirectInstallOptions MakeDirectCcuOptions(const TileXRDirectCcuPrepareOptions& publicOptions) +{ + TileXRCcuDirectInstallOptions options; + options.syncResourceCount = publicOptions.syncResourceCount; + options.sqeArgCount = publicOptions.sqeArgCount; + options.syncInstructionCount = publicOptions.syncInstructionCount; + options.bindingsPerSyncResource = publicOptions.bindingsPerSyncResource; + options.missionStartId = publicOptions.missionStartId; + options.instructionStartId = publicOptions.instructionStartId; + options.missionInstructionStartId = publicOptions.missionInstructionStartId; + options.xnStartId = publicOptions.xnStartId; + options.gsaStartId = publicOptions.gsaStartId; + options.remoteXnStartId = publicOptions.remoteXnStartId; + options.remoteXnCount = publicOptions.remoteXnCount; + options.ckeStartId = publicOptions.ckeStartId; + options.channelStartId = publicOptions.channelStartId; + options.localWaitCkeStartId = publicOptions.localWaitCkeStartId; + options.localWaitCkeCount = publicOptions.localWaitCkeCount; + options.remoteNotifyCkeStartId = publicOptions.remoteNotifyCkeStartId; + options.remoteNotifyCkeCount = publicOptions.remoteNotifyCkeCount; + options.repositoryInstallOptions.window = + RepositoryInstallWindowFromPublic(publicOptions.repositoryInstallWindow); + options.repositoryInstallOptions.dataLenMode = + RepositoryInstallDataLenModeFromPublic(publicOptions.repositoryInstallDataLenMode); + options.repositoryMemoryAllocMode = + RepositoryMemoryAllocModeFromPublic(publicOptions.repositoryMemoryAllocMode); + options.installOrder = InstallOrderFromPublic(publicOptions.installOrder); + options.deviceId = publicOptions.deviceId; + options.rank = publicOptions.rank; + const char* barrierMode = std::getenv(TILEXR_DIRECT_CCU_BARRIER_MODE_ENV); + if (barrierMode != nullptr && std::strcmp(barrierMode, "sync_cke") == 0) { + options.barrierMode = TileXRCcuBarrierMode::SyncCke; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "sync_cke_set_wait") == 0) { + options.barrierMode = TileXRCcuBarrierMode::SyncCkeSetWait; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "sync_cke_post_only") == 0) { + options.barrierMode = TileXRCcuBarrierMode::SyncCkePostOnly; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "local_cke") == 0) { + options.barrierMode = TileXRCcuBarrierMode::LocalCke; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "local_cke_post_only") == 0) { + options.barrierMode = TileXRCcuBarrierMode::LocalCkePostOnly; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "sync_xn_post_only") == 0) { + options.barrierMode = TileXRCcuBarrierMode::SyncXnPostOnly; + } else if (barrierMode != nullptr && std::strcmp(barrierMode, "sync_xn_load_post_only") == 0) { + options.barrierMode = TileXRCcuBarrierMode::SyncXnLoadPostOnly; + } + options.provider = (publicOptions.provider == nullptr || publicOptions.provider[0] == '\0') ? + TILEXR_DIRECT_CCU_PUBLIC_PROVIDER : + publicOptions.provider; + return options; +} + +TileXRDirectCcuPreparedTasks* PreparedHandle(TileXRDirectCcuPreparedTasksPtr prepared) +{ + return static_cast(prepared); +} + +TileXRCcuMemoryCopyDirection MemoryCopyDirectionFromPublic(uint32_t direction) +{ + return direction == TILEXR_DIRECT_CCU_MEMORY_COPY_LOCAL_TO_REMOTE ? + TileXRCcuMemoryCopyDirection::LocalToRemote : + TileXRCcuMemoryCopyDirection::RemoteToLocal; +} + +} // namespace + int TileXRCommInitRankLocal(int rankSize, int rank, TileXRCommPtr *comm) { TILEXR_LOG(INFO) << "using tilexr c++ api! rank" << rank; @@ -115,6 +292,29 @@ int TileXRCommInitRankWithDomain(int commDomain, int rankSize, int rank, TileXRC return TileXRCommInitRankWithCustDomainSize(commDomain, minBufferSize, rankSize, rank, comm); } +int TileXRCommInitRankDirectCcuWithDomain(int commDomain, int rankSize, int rank, TileXRCommPtr *comm) +{ + TILEXR_LOG(INFO) << "using tilexr direct CCU only api! rank" << rank; + if (comm == nullptr) { + TILEXR_LOG(ERROR) << "tilexr direct CCU only comm ptr is nullptr!"; + return TILEXR_ERROR_INTERNAL; + } + *comm = nullptr; + unique_ptr c(new (std::nothrow) TileXRComm(rank, rankSize, commDomain, TILEXR_COMM_BUFFER_SIZE)); + if (c == nullptr) { + TILEXR_LOG(ERROR) << "TileXRComm create failed for direct CCU only init. rank : " + << rank << ", rankSize : " << rankSize; + return TILEXR_ERROR_INTERNAL; + } + int ret = c->InitDirectCcuOnly(); + if (ret != TILEXR_SUCCESS) { + TILEXR_LOG(ERROR) << "tilexr direct CCU only init failed! ret: " << ret; + return ret; + } + *comm = c.release(); + return TILEXR_SUCCESS; +} + int TileXRGetCommArgsDev(TileXRCommPtr comm, GM_ADDR &commArgsPtr) { if (comm == nullptr) { @@ -212,6 +412,254 @@ int TileXRGetSDMAWorkspaceDev(TileXRCommPtr comm, GM_ADDR *workspace) return TILEXR_SUCCESS; } +int TileXRCommPrepareDirectCcu( + TileXRCommPtr comm, + const TileXRDirectCcuPrepareOptions *options, + TileXRDirectCcuPreparedTasksPtr *prepared, + TileXRDirectCcuPrepareReport *report) +{ + if (prepared != nullptr) { + *prepared = nullptr; + } + if (report != nullptr) { + *report = TileXRDirectCcuPrepareReport {}; + } + if (comm == nullptr || options == nullptr || prepared == nullptr) { + TILEXR_LOG(ERROR) << "TileXRCommPrepareDirectCcu invalid input"; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + std::unique_ptr handle(new (std::nothrow) TileXRDirectCcuPreparedTasks); + if (handle == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + + auto* tilexr = static_cast(comm); + TileXRCcuDirectInstallReport internalReport; + TileXRCcuDirectInstallOptions internalOptions = MakeDirectCcuOptions(*options); + const int ret = tilexr->PrepareDirectCcuInstallAttempt(internalOptions, &handle->attempt, &internalReport); + FillPublicPrepareReport(internalReport, report); + const bool installDiagnosticReady = + internalReport.pipelineBuilt && + internalReport.installAttempted && + internalReport.installSucceeded && + !internalReport.submitReady; + if (ret != TILEXR_SUCCESS && !installDiagnosticReady) { + return ret; + } + + *prepared = handle.release(); + return TILEXR_SUCCESS; +} + +int TileXRCommPrepareDirectCcuMemoryCopy( + TileXRCommPtr comm, + const TileXRDirectCcuMemoryCopyPrepareOptions *options, + TileXRDirectCcuPreparedTasksPtr *prepared, + TileXRDirectCcuPrepareReport *report) +{ + if (prepared != nullptr) { + *prepared = nullptr; + } + if (report != nullptr) { + *report = TileXRDirectCcuPrepareReport {}; + } + if (comm == nullptr || options == nullptr || prepared == nullptr) { + TILEXR_LOG(ERROR) << "TileXRCommPrepareDirectCcuMemoryCopy invalid input"; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + std::unique_ptr handle(new (std::nothrow) TileXRDirectCcuPreparedTasks); + if (handle == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + + TileXRCcuDirectInstallOptions internalOptions = MakeDirectCcuOptions(options->prepare); + internalOptions.sqeArgCount = 0; + internalOptions.syncResourceCount = 1; + internalOptions.syncInstructionCount = std::max( + internalOptions.syncInstructionCount, + TILEXR_DIRECT_CCU_MEMORY_COPY_INSTRUCTION_COUNT); + internalOptions.bindingsPerSyncResource = + internalOptions.bindingsPerSyncResource == 0 ? 1 : internalOptions.bindingsPerSyncResource; + const TileXRCcuMemoryCopyDirection direction = MemoryCopyDirectionFromPublic(options->direction); + + auto* tilexr = static_cast(comm); + TileXRCcuDirectInstallReport internalReport; + const int ret = tilexr->PrepareDirectCcuMemoryCopyInstallAttempt( + internalOptions, + options->localSourceAddr, + options->localDestinationAddr, + options->bytes, + options->peerRank, + direction, + &handle->attempt, + &internalReport); + FillPublicPrepareReport(internalReport, report); + const bool installDiagnosticReady = + internalReport.pipelineBuilt && + internalReport.installAttempted && + internalReport.installSucceeded && + !internalReport.submitReady; + if (ret != TILEXR_SUCCESS && !installDiagnosticReady) { + return ret; + } + + *prepared = handle.release(); + return TILEXR_SUCCESS; +} + +int TileXRDirectCcuGetPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t index, + TileXRDirectCcuTaskInfo *task) +{ + if (prepared == nullptr || task == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* handle = PreparedHandle(prepared); + if (index >= handle->attempt.submitTasks.size()) { + return TILEXR_ERROR_NOT_FOUND; + } + + const TileXRCcuTask& source = handle->attempt.submitTasks[index]; + *task = TileXRDirectCcuTaskInfo {}; + task->dieId = source.dieId; + task->missionId = source.missionId; + task->timeout = source.timeout; + task->instStartId = source.instStartId; + task->instCnt = source.instCnt; + task->key = source.key; + task->argSize = source.argSize; + std::memcpy(task->args, source.args, sizeof(task->args)); + return TILEXR_SUCCESS; +} + +int TileXRDirectCcuSubmitPrepared( + TileXRDirectCcuPreparedTasksPtr prepared, + void *stream, + TileXRDirectCcuSubmitReport *report) +{ + if (report != nullptr) { + *report = TileXRDirectCcuSubmitReport {}; + } + if (prepared == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* handle = PreparedHandle(prepared); + TileXRCcuDirectSubmitReport internalReport; + const int ret = TileXRCcuSubmitPreparedTasks( + handle->attempt.submitTasks, + stream, + nullptr, + nullptr, + &internalReport); + FillPublicSubmitReport(internalReport, report); + return ret; +} + +int TileXRDirectCcuSubmitPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t index, + void *stream, + TileXRDirectCcuSubmitReport *report) +{ + if (report != nullptr) { + *report = TileXRDirectCcuSubmitReport {}; + } + if (prepared == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* handle = PreparedHandle(prepared); + if (index >= handle->attempt.submitTasks.size()) { + if (report != nullptr) { + report->taskCount = static_cast(handle->attempt.submitTasks.size()); + CopyDirectCcuMessage("selected direct CCU submit task is missing", report->message); + } + return TILEXR_ERROR_NOT_FOUND; + } + + std::vector selectedTasks(1U); + selectedTasks[0] = handle->attempt.submitTasks[index]; + TileXRCcuDirectSubmitReport internalReport; + const int ret = TileXRCcuSubmitPreparedTasks( + selectedTasks, + stream, + nullptr, + nullptr, + &internalReport); + FillPublicSubmitReport(internalReport, report); + return ret; +} + +int TileXRCommReadDirectCcuInstructions( + TileXRCommPtr comm, + uint8_t dieId, + uint16_t instructionStartId, + uint32_t instructionCount, + TileXRDirectCcuInstructionWords *instructions, + TileXRDirectCcuInstructionReadbackReport *report) +{ + if (report != nullptr) { + *report = TileXRDirectCcuInstructionReadbackReport {}; + } + if (comm == nullptr || instructions == nullptr || instructionCount == 0 || + instructionCount > TILEXR_CCU_MAX_DATA_ARRAY_SIZE) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* tilexr = static_cast(comm); + TileXRCcuDriverAdapterReport internalReport; + const uint32_t instructionBytes = + instructionCount * static_cast(sizeof(TileXRDirectCcuInstructionWords)); + const int ret = tilexr->ReadDirectCcuInstructionsForDebug( + dieId, + instructionStartId, + instructions, + instructionCount, + instructionBytes, + &internalReport); + FillPublicInstructionReadbackReport(instructionCount, internalReport, report); + return ret; +} + +#if defined(TILEXR_CCU_TESTING) +extern "C" TileXRDirectCcuPreparedTasksPtr TileXRDirectCcuCreatePreparedForTest( + const TileXRDirectCcuTaskInfo* tasks, + uint32_t taskCount) +{ + if (tasks == nullptr || taskCount == 0) { + return nullptr; + } + std::unique_ptr handle(new (std::nothrow) TileXRDirectCcuPreparedTasks); + if (handle == nullptr) { + return nullptr; + } + handle->attempt.submitTasks.reserve(taskCount); + for (uint32_t i = 0; i < taskCount; ++i) { + TileXRCcuTask task {}; + task.dieId = tasks[i].dieId; + task.missionId = tasks[i].missionId; + task.timeout = tasks[i].timeout; + task.instStartId = tasks[i].instStartId; + task.instCnt = tasks[i].instCnt; + task.key = tasks[i].key; + task.argSize = tasks[i].argSize; + std::memcpy(task.args, tasks[i].args, sizeof(task.args)); + handle->attempt.submitTasks.push_back(task); + } + return handle.release(); +} +#endif + +int TileXRDirectCcuDestroyPrepared(TileXRDirectCcuPreparedTasksPtr prepared) +{ + if (prepared == nullptr) { + return TILEXR_INVALID_VALUE; + } + delete PreparedHandle(prepared); + return TILEXR_SUCCESS; +} + void TileXRPrintDFX2Log(TileXRCommPtr comm) { if (comm == nullptr) { diff --git a/src/comm/tilexr_comm.cpp b/src/comm/tilexr_comm.cpp index f627b9f..7c01300 100644 --- a/src/comm/tilexr_comm.cpp +++ b/src/comm/tilexr_comm.cpp @@ -9,6 +9,8 @@ */ #include "tilexr_comm.h" #include "tilexr_internal.h" +#include "ccu/tilexr_ccu_memory_program.h" +#include "ccu/tilexr_ccu_repository.h" #include "sdma/tilexr_sdma_transport.h" #include "udma/tilexr_udma_transport.h" @@ -22,13 +24,14 @@ #include #include #include +#include #include "tilexr_log.h" #include "tools/socket/tilexr_sock_exchange.h" -#include "runtime/kernel.h" #include "runtime/mem.h" #include "runtime/dev.h" +#include "runtime/rts/rts_device.h" #include "runtime/rt_ffts.h" enum TopologyType : int { @@ -47,14 +50,145 @@ using namespace chrono; namespace TileXR { constexpr int HCCL_IPC_PID_ARRAY_SIZE = 1; // 固定每次只传一个PID数据 constexpr int TILEXR_INIT_TIMEOUT = 600; +constexpr uint32_t TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT = 7U; static map g_localPeerMemMap; static map g_devList; +struct TileXRThreadAllGatherState { + std::vector data[TILEXR_MAX_RANK_SIZE]; + uint64_t arrivals = 0; + uint64_t departures = 0; + size_t bytes = 0; +}; +static map g_directCcuAllGatherStates; static std::mutex g_mtx; static std::mutex g_udmaMtx; static bool g_udmaUnavailable = false; +static std::mutex g_ccuDirectRuntimeMtx; +static bool g_ccuDirectRuntimeUnavailable = false; +static std::string g_ccuDirectRuntimeUnavailableMessage; static std::mutex g_sdmaMtx; static bool g_sdmaUnavailable = false; +uint8_t SelectDirectCcuInstallDieId() +{ + const char *text = std::getenv("TILEXR_CCU_DIRECT_INSTALL_DIE_ID"); + if (text == nullptr || text[0] == '\0') { + return 0; + } + char *end = nullptr; + const unsigned long parsed = std::strtoul(text, &end, 0); + if (end == text || *end != '\0' || parsed > 1UL) { + return 0; + } + return static_cast(parsed); +} + +uint32_t SelectDirectCcuPeerLocalXnOffset(size_t peerLocalIndex, uint32_t syncIndex, size_t peerRouteCount) +{ + if (peerRouteCount == 0) { + return 0; + } + return static_cast(peerLocalIndex) + + static_cast(syncIndex / peerRouteCount) * static_cast(peerRouteCount); +} + +uint32_t SelectDirectCcuChannelBoundRemoteXnOffset(size_t peerLocalIndex, uint32_t syncIndex, size_t peerRouteCount) +{ + return SelectDirectCcuPeerLocalXnOffset(peerLocalIndex, syncIndex, peerRouteCount); +} + +uint16_t DirectCcuRemoteXnProofSpan(uint16_t syncRouteCount) +{ + if (syncRouteCount == 0) { + return 0; + } + return syncRouteCount; +} + +uint16_t SelectDirectCcuChannelBoundRemoteXnId( + uint16_t remoteXnStartId, + size_t peerLocalIndex, + uint32_t syncIndex, + size_t peerRouteCount) +{ + return static_cast( + static_cast(remoteXnStartId) + + SelectDirectCcuChannelBoundRemoteXnOffset(peerLocalIndex, syncIndex, peerRouteCount)); +} + +uint16_t SelectDirectCcuRemoteNotifyCkeId(uint16_t remoteNotifyCkeStartId, size_t routeIndex) +{ + return static_cast(static_cast(remoteNotifyCkeStartId) + routeIndex); +} + +std::string ProcessDirectCcuRuntimeUnavailableMessage() +{ + lock_guard lock(g_ccuDirectRuntimeMtx); + if (!g_ccuDirectRuntimeUnavailable) { + return {}; + } + return g_ccuDirectRuntimeUnavailableMessage.empty() ? + "direct CCU runtime unavailable after process-level init failure" : + "direct CCU runtime unavailable after process-level init failure: " + + g_ccuDirectRuntimeUnavailableMessage; +} + +struct DirectCcuMemoryCopyEndpoint { + uint64_t sourceAddr = 0; + uint64_t sourceToken = 0; + uint64_t destinationAddr = 0; + uint64_t destinationToken = 0; + uint64_t bytes = 0; + uint32_t rank = 0; + uint32_t valid = 0; +}; + +int QueryDirectCcuProcessMemoryToken(uint64_t addr, uint64_t bytes, uint64_t *packedToken) +{ + if (addr == 0 || bytes == 0 || packedToken == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *packedToken = 0; + rtMemUbTokenInfo info {}; + info.va = addr; + info.size = bytes; + const rtError_t ret = rtUbDevQueryInfo(QUERY_PROCESS_TOKEN, &info); + if (ret != RT_ERROR_NONE) { + return TILEXR_ERROR_MKIRT; + } + constexpr uint32_t tokenIdRightShift = 8U; + const uint32_t tokenId = info.tokenId >> tokenIdRightShift; + *packedToken = TileXRCcuPackMemoryToken(tokenId, info.tokenValue, true); + return *packedToken == 0 ? TILEXR_ERROR_NOT_FOUND : TILEXR_SUCCESS; +} + +int BuildDirectCcuLocalMemoryCopyEndpoint( + uint32_t rank, + uint64_t sourceAddr, + uint64_t destinationAddr, + uint64_t bytes, + DirectCcuMemoryCopyEndpoint *endpoint) +{ + if (endpoint == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *endpoint = DirectCcuMemoryCopyEndpoint {}; + endpoint->rank = rank; + endpoint->bytes = bytes; + endpoint->sourceAddr = sourceAddr; + endpoint->destinationAddr = destinationAddr; + int ret = QueryDirectCcuProcessMemoryToken(sourceAddr, bytes, &endpoint->sourceToken); + if (ret != TILEXR_SUCCESS) { + return ret; + } + ret = QueryDirectCcuProcessMemoryToken(destinationAddr, bytes, &endpoint->destinationToken); + if (ret != TILEXR_SUCCESS) { + return ret; + } + endpoint->valid = 1; + return TILEXR_SUCCESS; +} + // 如果是互联的链路,返回false; 对910B2C那些不互联的链路,返回true bool SkipUnusedChannel910B2C(int curRank, int peerRank, ChipName chipName) @@ -170,6 +304,63 @@ int TileXRComm::InitUDMA() return TILEXR_SUCCESS; } +int TileXRComm::InitDirectCcuRuntime() +{ + if (rankSize_ <= 1) { + TILEXR_LOG(INFO) << "direct CCU runtime skipped for single-rank communicator"; + return TILEXR_SUCCESS; + } + + lock_guard lock(g_ccuDirectRuntimeMtx); + if (g_ccuDirectRuntimeUnavailable) { + TILEXR_LOG(INFO) << "direct CCU runtime skipped after previous init failure"; + return TILEXR_SUCCESS; + } + + ccuDirectRuntime_.reset(new (nothrow) TileXRCcuDirectRuntime()); + if (ccuDirectRuntime_ == nullptr) { + TILEXR_LOG(WARN) << "TileXRCcuDirectRuntime allocation failed, direct CCU disabled"; + return TILEXR_SUCCESS; + } + + TileXRCcuDirectRuntimeOptions options {}; + options.rank = rank_; + options.rankSize = rankSize_; + options.devId = devId_; + options.allGather = &TileXRComm::DirectCcuAllGatherCallback; + options.allGatherUserData = this; + TileXRCcuDirectRuntimeReport runtimeReport; + const int ret = ccuDirectRuntime_->Init(options, &runtimeReport); + if (ret != TILEXR_SUCCESS || !ccuDirectRuntime_->IsAvailable()) { + TILEXR_LOG(WARN) << "TileXR direct CCU runtime init failed: " << ret + << ", logicDevId " << runtimeReport.logicDevId + << ", devicePhyId " << runtimeReport.devicePhyId + << ", hdcType " << runtimeReport.hdcType + << ", raInitialized " << (runtimeReport.raInitialized ? 1 : 0) + << ", ccuTlvInitialized " << (runtimeReport.ccuTlvInitialized ? 1 : 0) + << ", " << runtimeReport.message << ", direct CCU disabled"; + g_ccuDirectRuntimeUnavailable = true; + g_ccuDirectRuntimeUnavailableMessage = runtimeReport.message; + ResetDirectCcuBasicInfo(); + ccuDirectRuntime_.reset(); + return TILEXR_SUCCESS; + } + + const int ccuInfoRet = RefreshDirectCcuBasicInfo(0); + if (ccuInfoRet != TILEXR_SUCCESS && ccuInfoRet != TILEXR_ERROR_NOT_FOUND) { + TILEXR_LOG(WARN) << "direct CCU basic info refresh failed after runtime init: " << ccuInfoRet + << ", " << directCcuBasicInfoReport_.message; + } + + TILEXR_LOG(INFO) << "InitDirectCcuRuntime success, rank " << rank_ << "/" << rankSize_ + << " logicDevId " << runtimeReport.logicDevId + << " devicePhyId " << runtimeReport.devicePhyId + << " hdcType " << runtimeReport.hdcType + << " raInitialized " << (runtimeReport.raInitialized ? 1 : 0) + << " ccuTlvInitialized " << (runtimeReport.ccuTlvInitialized ? 1 : 0); + return TILEXR_SUCCESS; +} + int TileXRComm::InitSDMA() { { @@ -315,6 +506,23 @@ void TileXRComm::FreeUDMARegistry() udmaRegistry_ = TileXRUDMARegistry {}; } +void TileXRComm::ResetDirectCcuBasicInfo() +{ + directCcuBasicInfoValid_ = false; + directCcuBasicInfoStatus_ = TILEXR_ERROR_NOT_FOUND; + directCcuBasicInfo_ = TileXRCcuBasicInfo {}; + directCcuBasicInfoReport_ = TileXRCcuDriverAdapterReport {}; +} + +void TileXRComm::ResetDirectCcuLowerLayerPlan() +{ + directCcuLowerLayerPlanValid_ = false; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + directCcuLowerLayerSnapshot_ = TileXRCcuLowerLayerTransportSnapshot {}; + directCcuLowerLayerPlan_ = TileXRCcuLowerLayerInstallPlan {}; + directCcuLowerLayerPlanReport_ = TileXRCcuLowerLayerPlanBuilderReport {}; +} + int TileXRComm::RegisterUDMAMemory(GM_ADDR localPtr, size_t bytes, TileXRUDMAMemHandle *handle) { if (!inited_) { @@ -402,8 +610,10 @@ int TileXRComm::RegisterUDMAMemory(GM_ADDR localPtr, size_t bytes, TileXRUDMAMem udmaTransport_->UnregisterMemory(localPtr); udmaRegisteredPtr_ = nullptr; FreeUDMARegistry(); + return ret; } - return ret; + + return TILEXR_SUCCESS; } int TileXRComm::UnregisterUDMAMemory(TileXRUDMAMemHandle handle) @@ -432,6 +642,796 @@ const TileXRUDMARegistry* TileXRComm::GetUDMARegistryHost() const return UDMARegistryValid(&udmaRegistry_, rankSize_) ? &udmaRegistry_ : nullptr; } +int TileXRComm::RefreshDirectCcuBasicInfo(uint8_t dieId) +{ + ResetDirectCcuBasicInfo(); + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + directCcuBasicInfoReport_.message = "direct CCU runtime is unavailable for basic info"; + directCcuBasicInfoStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuBasicInfoStatus_; + } + + TileXRCcuBasicInfo basicInfo; + TileXRCcuDriverAdapterReport report; + const int ret = ccuDirectRuntime_->QueryBasicInfo(dieId, &basicInfo, &report); + directCcuBasicInfoReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuBasicInfoStatus_ = ret; + return directCcuBasicInfoStatus_; + } + + directCcuBasicInfo_ = basicInfo; + directCcuBasicInfoReport_.message = "direct CCU basic info cached"; + directCcuBasicInfoValid_ = true; + directCcuBasicInfoStatus_ = TILEXR_SUCCESS; + return TILEXR_SUCCESS; +} + +bool TileXRComm::HasDirectCcuBasicInfo() const +{ + return directCcuBasicInfoValid_; +} + +int TileXRComm::GetDirectCcuBasicInfoStatus() const +{ + return directCcuBasicInfoStatus_; +} + +const TileXRCcuBasicInfo *TileXRComm::GetDirectCcuBasicInfo() const +{ + return directCcuBasicInfoValid_ ? &directCcuBasicInfo_ : nullptr; +} + +const TileXRCcuDriverAdapterReport &TileXRComm::GetDirectCcuBasicInfoReport() const +{ + return directCcuBasicInfoReport_; +} + +int TileXRComm::ConfigureDirectCcuLowerLayerTemplate( + const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot) +{ + directCcuLowerLayerTemplate_ = templateSnapshot; + directCcuLowerLayerTemplateConfigured_ = true; + return RefreshDirectCcuLowerLayerPlan(); +} + +int TileXRComm::ConfigureDirectCcuVerifiedEndpointRoutes( + const std::vector &verifiedRoutes) +{ + TileXRCcuLowerLayerTransportSnapshot validationSnapshot; + validationSnapshot.routes = verifiedRoutes; + TileXRCcuLowerLayerPlanBuilderReport report; + int ret = TileXRCcuOverlayVerifiedEndpointRoutes(verifiedRoutes, &validationSnapshot, &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return ret; + } + + directCcuVerifiedEndpointRoutes_ = verifiedRoutes; + if (directCcuLowerLayerTemplateConfigured_) { + return RefreshDirectCcuLowerLayerPlan(); + } + return TILEXR_SUCCESS; +} + +int TileXRComm::ConfigureDirectCcuLocalVerifiedEndpointRoute( + const TileXRCcuLowerLayerTransportRoute &route) +{ + TileXRCcuLowerLayerTransportSnapshot validationSnapshot; + validationSnapshot.routes.push_back(route); + TileXRCcuLowerLayerPlanBuilderReport report; + std::vector routes {route}; + int ret = TileXRCcuOverlayVerifiedEndpointRoutes(routes, &validationSnapshot, &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLocalVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute {}; + directCcuLocalVerifiedEndpointRouteValid_ = false; + directCcuLowerLayerPlanStatus_ = ret; + return ret; + } + + directCcuLocalVerifiedEndpointRoute_ = route; + directCcuLocalVerifiedEndpointRouteValid_ = true; + if (ccuDirectRuntime_ != nullptr && ccuDirectRuntime_->IsAvailable()) { + return ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute(route); + } + return TILEXR_SUCCESS; +} + +int TileXRComm::ConfigureDirectCcuLowerLayerTemplateFromAllocation( + const TileXRCcuResourceAllocation &allocation, + const std::vector &remoteCcuBuffers) +{ + ResetDirectCcuLowerLayerPlan(); + if (!directCcuBasicInfoValid_) { + directCcuLowerLayerPlanReport_.message = + "direct CCU basic info is unavailable for lower-layer transport template"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLowerLayerTransportSnapshot templateSnapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + int ret = TileXRCcuBuildLowerLayerTransportTemplate( + directCcuBasicInfo_, + allocation, + remoteCcuBuffers, + &templateSnapshot, + &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + ret = TileXRCcuOverlayVerifiedEndpointRoutes( + directCcuVerifiedEndpointRoutes_, + &templateSnapshot, + &directCcuLowerLayerPlanReport_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + directCcuLowerLayerTemplate_ = templateSnapshot; + directCcuLowerLayerTemplateConfigured_ = true; + return RefreshDirectCcuLowerLayerPlan(); +} + +int TileXRComm::PrepareDirectCcuLowerLayerTemplateFromAllocation( + const TileXRCcuResourceAllocation &allocation) +{ + ResetDirectCcuLowerLayerPlan(); + if (!directCcuBasicInfoValid_) { + directCcuLowerLayerPlanReport_.message = + "direct CCU basic info is unavailable for lower-layer transport template"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + directCcuLowerLayerPlanReport_.message = + "direct CCU runtime is unavailable for resource window registration"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + + int ret = ccuDirectRuntime_->RegisterCcuResourceRmaBuffer(directCcuBasicInfo_.resourceAddr); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to register direct CCU resource window"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLocalResourceWindowInfo localCcuResourceWindow; + ret = ccuDirectRuntime_->ExportLocalCcuRmaBuffer(&localCcuResourceWindow); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to export direct CCU local resource window token"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + if (directCcuLocalVerifiedEndpointRouteValid_) { + ret = ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute(directCcuLocalVerifiedEndpointRoute_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to configure direct CCU local verified endpoint route"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + } else { + TileXRCcuDirectRuntimeReport endpointRouteReport; + ret = ccuDirectRuntime_->RefreshLocalVerifiedEndpointRoute(&endpointRouteReport); + if (ret != TILEXR_SUCCESS && ret != TILEXR_ERROR_NOT_FOUND) { + TILEXR_LOG(WARN) << "direct CCU local endpoint route collection failed closed: " + << ret << ", " << endpointRouteReport.message; + } + } + + std::vector remoteCcuBuffers; + ret = ccuDirectRuntime_->ExportRemoteCcuRmaBuffers(&remoteCcuBuffers); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to export direct CCU peer resource window tokens"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + ret = ExchangeDirectCcuRemoteNotifyCke(allocation, &remoteCcuBuffers, &directCcuLowerLayerPlanReport_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLowerLayerTransportSnapshot templateSnapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + ret = TileXRCcuBuildLowerLayerTransportTemplate( + directCcuBasicInfo_, + allocation, + remoteCcuBuffers, + &templateSnapshot, + &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + templateSnapshot.msidToken.dieId = directCcuBasicInfo_.dieId; + templateSnapshot.msidToken.msId = directCcuBasicInfo_.msId; + templateSnapshot.msidToken.tokenId = localCcuResourceWindow.tokenId; + templateSnapshot.msidToken.tokenValue = localCcuResourceWindow.tokenValue; + templateSnapshot.msidToken.valid = true; + ret = TileXRCcuOverlayVerifiedEndpointRoutes( + directCcuVerifiedEndpointRoutes_, + &templateSnapshot, + &directCcuLowerLayerPlanReport_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + directCcuLowerLayerTemplate_ = templateSnapshot; + directCcuLowerLayerTemplateConfigured_ = true; + return RefreshDirectCcuLowerLayerPlan(); +} + +int TileXRComm::FillDirectCcuLowerLayerPlanFromAllocation( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report) +{ + if (plan == nullptr || report == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + int ret = PrepareDirectCcuLowerLayerTemplateFromAllocation(allocation); + if (ret != TILEXR_SUCCESS) { + *report = directCcuLowerLayerPlanReport_; + return ret; + } + if (!directCcuLowerLayerPlanValid_) { + *report = directCcuLowerLayerPlanReport_; + return TILEXR_ERROR_NOT_FOUND; + } + *plan = directCcuLowerLayerPlan_; + *report = directCcuLowerLayerPlanReport_; + return TILEXR_SUCCESS; +} + +int TileXRComm::ExchangeDirectCcuRemoteNotifyCke( + const TileXRCcuResourceAllocation &allocation, + std::vector *remoteCcuBuffers, + TileXRCcuLowerLayerPlanBuilderReport *report) +{ + if (remoteCcuBuffers == nullptr) { + if (report != nullptr) { + report->message = "missing direct CCU remote notify CKE exchange inputs"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (rankSize_ <= 1 || rank_ < 0 || rank_ >= rankSize_) { + if (report != nullptr) { + report->message = "invalid direct CCU peer XN/CKE exchange shape"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const size_t peerRouteCount = static_cast(rankSize_ - 1); + const size_t syncRouteCount = allocation.remoteXn.num; + if (allocation.localXn.num == 0 || + allocation.localWaitCke.num == 0 || + allocation.remoteNotifyCke.num == 0 || + allocation.remoteXn.num < static_cast(rankSize_ - 1) || + allocation.localWaitCke.num < allocation.remoteXn.num || + allocation.remoteNotifyCke.num < allocation.remoteXn.num || + allocation.channels.num == 0 || + remoteCcuBuffers->size() != peerRouteCount) { + if (report != nullptr) { + report->message = "invalid direct CCU peer XN/CKE exchange shape"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + struct PeerResourceExchange { + uint16_t localXnStartId; + uint16_t localXnCount; + uint16_t remoteXnStartId; + uint16_t remoteXnCount; + uint16_t localWaitCkeStartId; + uint16_t localWaitCkeCount; + uint16_t remoteNotifyCkeStartId; + uint16_t remoteNotifyCkeCount; + uint16_t channelStartId; + uint16_t channelCount; + }; + PeerResourceExchange local { + allocation.localXn.startId, + allocation.localXn.num, + allocation.remoteXn.startId, + DirectCcuRemoteXnProofSpan(allocation.remoteXn.num), + allocation.localWaitCke.startId, + allocation.localWaitCke.num, + allocation.remoteNotifyCke.startId, + allocation.remoteNotifyCke.num, + allocation.channels.startId, + allocation.channels.num, + }; + std::vector all(rankSize_); + const int ret = DirectCcuAllGatherCallback(&local, sizeof(local), all.data(), this); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = "failed to exchange direct CCU peer XN/CKE resources"; + } + return ret; + } + + std::vector peerRanks; + peerRanks.reserve(peerRouteCount); + for (int peer = 0; peer < rankSize_; ++peer) { + if (peer != rank_) { + peerRanks.push_back(peer); + } + } + if (peerRanks.size() != peerRouteCount) { + if (report != nullptr) { + report->message = "invalid direct CCU peer XN/CKE exchange shape"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + std::vector peerCcuBuffers = *remoteCcuBuffers; + remoteCcuBuffers->assign(syncRouteCount, TileXRCcuRemoteCcuBufferInfo{}); + + size_t routeIndex = 0; + for (uint32_t syncIndex = 0; syncIndex < allocation.remoteXn.num; ++syncIndex) { + const size_t peerBufferIndex = syncIndex % peerRouteCount; + const int peer = peerRanks[peerBufferIndex]; + const PeerResourceExchange &peerResources = all[peer]; + const size_t peerLocalIndex = static_cast(rank_ < peer ? rank_ : rank_ - 1); + const uint32_t peerLocalXnOffset = + SelectDirectCcuPeerLocalXnOffset(peerLocalIndex, syncIndex, peerRouteCount); + const uint32_t selectedRemoteXnOffset = + SelectDirectCcuChannelBoundRemoteXnOffset(peerLocalIndex, syncIndex, peerRouteCount); + const uint32_t peerLocalWaitCkeOffset = routeIndex; + if (peerResources.localXnCount == 0 || + peerResources.remoteXnCount == 0 || + peerResources.localWaitCkeCount == 0 || + peerResources.channelCount == 0 || + peerLocalXnOffset >= peerResources.localXnCount || + selectedRemoteXnOffset >= peerResources.remoteXnCount || + peerLocalIndex >= peerResources.channelCount || + peerLocalWaitCkeOffset >= peerResources.localWaitCkeCount) { + if (report != nullptr) { + report->message = "peer direct CCU local XN/CKE resources are incomplete"; + } + return TILEXR_ERROR_NOT_FOUND; + } + uint16_t channelBoundRemoteXnId = SelectDirectCcuChannelBoundRemoteXnId( + peerResources.remoteXnStartId, + peerLocalIndex, + syncIndex, + peerRouteCount); + const uint16_t peerLocalXnId = + static_cast(static_cast(peerResources.localXnStartId) + peerLocalXnOffset); + uint16_t remoteNotifyCke = + static_cast(static_cast(peerResources.localWaitCkeStartId) + + peerLocalWaitCkeOffset); + (*remoteCcuBuffers)[routeIndex] = peerCcuBuffers[peerBufferIndex]; + (*remoteCcuBuffers)[routeIndex].remoteXnId = channelBoundRemoteXnId; + (*remoteCcuBuffers)[routeIndex].remoteNotifyCke = remoteNotifyCke; + const bool peerLocalXnOwnerVerified = + static_cast(peerLocalXnId) >= peerResources.localXnStartId && + static_cast(peerLocalXnId) < + static_cast(peerResources.localXnStartId) + peerResources.localXnCount; + const bool notifyCkeOwnerVerified = + static_cast(remoteNotifyCke) >= peerResources.localWaitCkeStartId && + static_cast(remoteNotifyCke) < + static_cast(peerResources.localWaitCkeStartId) + peerResources.localWaitCkeCount; + const bool localChannelOwnerVerified = + allocation.channels.num != 0 && + peerLocalXnOwnerVerified && + static_cast(channelBoundRemoteXnId) >= peerResources.remoteXnStartId && + static_cast(channelBoundRemoteXnId) < + static_cast(peerResources.remoteXnStartId) + peerResources.remoteXnCount && + routeIndex < allocation.channels.num && + peerResources.channelStartId != 0 && + peerLocalIndex < peerResources.channelCount; + const bool transportResourceExchangeVerified = + notifyCkeOwnerVerified && + allocation.localWaitCke.num != 0 && + routeIndex < allocation.localWaitCke.num && + peerLocalWaitCkeOffset < peerResources.localWaitCkeCount; + (*remoteCcuBuffers)[routeIndex].channelResourceOwnerVerified = localChannelOwnerVerified; + (*remoteCcuBuffers)[routeIndex].transportResourceExchangeVerified = transportResourceExchangeVerified; + ++routeIndex; + } + return TILEXR_SUCCESS; +} + +int TileXRComm::DirectCcuAllGatherCallback( + const void *sendBuf, + size_t sendBytes, + void *recvBuf, + void *userData) +{ + auto *comm = static_cast(userData); + if (comm == nullptr || sendBuf == nullptr || recvBuf == nullptr || sendBytes == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (comm->socketExchange_ == nullptr) { + return comm->DirectCcuThreadAllGather(sendBuf, sendBytes, recvBuf); + } + return comm->socketExchange_->AllGather( + static_cast(sendBuf), + sendBytes, + static_cast(recvBuf)); +} + +int TileXRComm::DirectCcuThreadAllGather(const void *sendBuf, size_t sendBytes, void *recvBuf) +{ + if (sendBuf == nullptr || recvBuf == nullptr || sendBytes == 0 || rank_ < 0 || + rank_ >= rankSize_ || rankSize_ <= 0 || uid_.empty()) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const uint64_t round = directCcuThreadAllGatherRound_++; + const std::string key = uid_ + ":ccu:" + std::to_string(round); + auto start = high_resolution_clock::now(); + for (;;) { + { + lock_guard lock(g_mtx); + auto &state = g_directCcuAllGatherStates[key]; + if (state.bytes == 0) { + state.bytes = sendBytes; + } else if (state.bytes != sendBytes) { + g_directCcuAllGatherStates.erase(key); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (state.data[rank_].empty()) { + state.data[rank_].resize(sendBytes); + std::memcpy(state.data[rank_].data(), sendBuf, sendBytes); + ++state.arrivals; + } + if (state.arrivals == static_cast(rankSize_)) { + auto *output = static_cast(recvBuf); + for (int i = 0; i < rankSize_; ++i) { + std::memcpy(output + static_cast(i) * sendBytes, state.data[i].data(), sendBytes); + } + ++state.departures; + if (state.departures == static_cast(rankSize_)) { + g_directCcuAllGatherStates.erase(key); + } + return TILEXR_SUCCESS; + } + } + const std::string processUnavailableMessage = ProcessDirectCcuRuntimeUnavailableMessage(); + if (!processUnavailableMessage.empty()) { + lock_guard lock(g_mtx); + g_directCcuAllGatherStates.erase(key); + TILEXR_LOG(ERROR) << "direct CCU thread allgather abort rank " << rank_ << "/" << rankSize_ + << " uid " << uid_ << " round " << round << ", " + << processUnavailableMessage; + return TILEXR_ERROR_NOT_FOUND; + } + this_thread::sleep_for(1ms); + auto elapsed = duration_cast(high_resolution_clock::now() - start); + if (elapsed.count() > TILEXR_INIT_TIMEOUT) { + lock_guard lock(g_mtx); + g_directCcuAllGatherStates.erase(key); + TILEXR_LOG(ERROR) << "direct CCU thread allgather timeout rank " << rank_ << "/" << rankSize_ + << " uid " << uid_ << " round " << round; + return TILEXR_ERROR_TIMEOUT; + } + } +} + +int TileXRComm::PrepareDirectCcuLowerLayerPlanCallback( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report, + void *userData) +{ + auto *comm = static_cast(userData); + if (comm == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return comm->FillDirectCcuLowerLayerPlanFromAllocation(allocation, plan, report); +} + +int TileXRComm::PrepareDirectCcuInstallAttempt( + const TileXRCcuDirectInstallOptions &options, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!inited_) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = "TileXRComm is not initialized for direct CCU install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const std::string processUnavailableMessage = ProcessDirectCcuRuntimeUnavailableMessage(); + if (!processUnavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = processUnavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + if (!directCcuBasicInfoValid_ || directCcuBasicInfo_.dieId != installDieId) { + const int ret = RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = directCcuBasicInfoReport_.message; + } + return ret; + } + } + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = "direct CCU runtime is unavailable for install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + int ret = ccuDirectRuntime_->CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = adapterReport.message; + } + return ret; + } + + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = &directCcuBasicInfo_; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRComm::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = this; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu"; + } + + return TileXRCcuRunDirectInstallAttempt(next, attempt, report); +} + +int TileXRComm::PrepareDirectCcuMemoryCopyInstallAttempt( + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuMemoryCopyDirection direction, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!inited_) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRComm is not initialized for direct CCU memory copy install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (localSourceAddr == 0 || localDestinationAddr == 0 || bytes == 0 || + peerRank >= static_cast(rankSize_) || peerRank == static_cast(rank_)) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU memory copy endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const std::string processUnavailableMessage = ProcessDirectCcuRuntimeUnavailableMessage(); + if (!processUnavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = processUnavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + if (!directCcuBasicInfoValid_ || directCcuBasicInfo_.dieId != installDieId) { + const int ret = RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = directCcuBasicInfoReport_.message; + } + return ret; + } + } + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for memory copy install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + DirectCcuMemoryCopyEndpoint localEndpoint; + int ret = BuildDirectCcuLocalMemoryCopyEndpoint( + static_cast(rank_), + localSourceAddr, + localDestinationAddr, + bytes, + &localEndpoint); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to query direct CCU memory copy local buffer token"; + } + return ret; + } + + std::vector allEndpoints(static_cast(rankSize_)); + ret = DirectCcuAllGatherCallback( + &localEndpoint, + sizeof(localEndpoint), + allEndpoints.data(), + this); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to exchange direct CCU memory copy peer endpoints"; + } + return ret; + } + const DirectCcuMemoryCopyEndpoint &peerEndpoint = allEndpoints[peerRank]; + if (peerEndpoint.valid == 0 || peerEndpoint.bytes != bytes) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU memory copy peer endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuDirectMemoryCopySpec memoryCopy; + memoryCopy.direction = direction; + memoryCopy.lengthBytes = bytes; + if (direction == TileXRCcuMemoryCopyDirection::RemoteToLocal) { + memoryCopy.localAddr = localEndpoint.destinationAddr; + memoryCopy.localToken = localEndpoint.destinationToken; + memoryCopy.remoteAddr = peerEndpoint.sourceAddr; + memoryCopy.remoteToken = peerEndpoint.sourceToken; + } else { + memoryCopy.localAddr = localEndpoint.sourceAddr; + memoryCopy.localToken = localEndpoint.sourceToken; + memoryCopy.remoteAddr = peerEndpoint.destinationAddr; + memoryCopy.remoteToken = peerEndpoint.destinationToken; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + ret = ccuDirectRuntime_->CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = &directCcuBasicInfo_; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRComm::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = this; + next.sqeArgCount = 0; + next.syncResourceCount = 1; + next.syncInstructionCount = std::max( + next.syncInstructionCount, + TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT); + next.bindingsPerSyncResource = next.bindingsPerSyncResource == 0 ? 1 : next.bindingsPerSyncResource; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu-memory-copy"; + } + + return TileXRCcuRunDirectMemoryCopyInstallAttempt(next, memoryCopy, attempt, report); +} + +int TileXRComm::RefreshDirectCcuLowerLayerPlan() +{ + ResetDirectCcuLowerLayerPlan(); + if (!directCcuLowerLayerTemplateConfigured_) { + directCcuLowerLayerPlanReport_.message = "direct CCU lower-layer template is not configured"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + directCcuLowerLayerPlanReport_.message = "direct CCU runtime is unavailable for lower-layer planning"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLowerLayerTransportSnapshot snapshot; + int ret = ccuDirectRuntime_->ExportLowerLayerTransportSnapshot(directCcuLowerLayerTemplate_, &snapshot); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to export direct CCU lower-layer transport snapshot"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + ret = TileXRCcuOverlayVerifiedEndpointRoutes( + directCcuVerifiedEndpointRoutes_, + &snapshot, + &directCcuLowerLayerPlanReport_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + ret = TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + directCcuLowerLayerSnapshot_ = snapshot; + directCcuLowerLayerPlan_ = plan; + directCcuLowerLayerPlanReport_.message = "direct CCU lower-layer install plan cached"; + directCcuLowerLayerPlanValid_ = true; + directCcuLowerLayerPlanStatus_ = TILEXR_SUCCESS; + return TILEXR_SUCCESS; +} + +bool TileXRComm::HasDirectCcuLowerLayerPlan() const +{ + return directCcuLowerLayerPlanValid_; +} + +int TileXRComm::GetDirectCcuLowerLayerPlanStatus() const +{ + return directCcuLowerLayerPlanStatus_; +} + +const TileXRCcuLowerLayerPlanBuilderReport &TileXRComm::GetDirectCcuLowerLayerPlanReport() const +{ + return directCcuLowerLayerPlanReport_; +} + +const TileXRCcuLowerLayerInstallPlan *TileXRComm::GetDirectCcuLowerLayerPlan() const +{ + return directCcuLowerLayerPlanValid_ ? &directCcuLowerLayerPlan_ : nullptr; +} + +int TileXRComm::ReadDirectCcuInstructionsForDebug( + uint8_t dieId, + uint16_t instructionStartId, + void *instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport *report) +{ + if (report != nullptr) { + *report = TileXRCcuDriverAdapterReport{}; + } + if (!inited_) { + if (report != nullptr) { + report->message = "TileXRComm is not initialized for direct CCU instruction readback"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + if (report != nullptr) { + report->message = "direct CCU runtime is unavailable for instruction readback"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuDriverAdapter adapter; + int ret = ccuDirectRuntime_->CreateDriverAdapter(&adapter, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + return adapter.ReadInstructions(dieId, instructionStartId, instructions, instructionCount, instructionBytes, report); +} + int TileXRComm::InitCommon() { // enable peer device @@ -533,6 +1533,10 @@ int TileXRComm::Init() if (ret != TILEXR_SUCCESS) { return ret; } + ret = InitDirectCcuRuntime(); + if (ret != TILEXR_SUCCESS) { + return ret; + } ret = InitSDMA(); if (ret != TILEXR_SUCCESS) { return ret; @@ -550,6 +1554,47 @@ int TileXRComm::Init() return TILEXR_SUCCESS; } +int TileXRComm::InitDirectCcuOnly() +{ + if (inited_) { + return TILEXR_SUCCESS; + } + if (rank_ < 0 || rank_ >= rankSize_ || rankSize_ <= 0 || rankSize_ > TILEXR_MAX_RANK_SIZE) { + TILEXR_LOG(ERROR) << "The rank is invalid for direct CCU only init! rank:" << rank_ + << " rankSize:" << rankSize_; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (TileXRSockExchange::CheckValid(commId_)) { + socketExchange_ = new (nothrow) TileXRSockExchange(rank_, rankSize_, commId_); + } else { + socketExchange_ = new (nothrow) TileXRSockExchange(rank_, rankSize_, commDomain_); + } + if (socketExchange_ == nullptr) { + TILEXR_LOG(ERROR) << "TileXRSockExchange create failed for direct CCU only init. rank : " + << rank_ << " rankSize:" << rankSize_; + return TILEXR_ERROR_INTERNAL; + } + + int ret = GetDev(); + if (ret != TILEXR_SUCCESS) { + TILEXR_LOG(ERROR) << "direct CCU only init context failed! ret: " << ret; + return ret; + } + TILEXR_LOG(INFO) << "direct CCU only init rank " << rank_ << "/" << rankSize_ + << " running devId:" << devId_; + + if (InitCommon() != TILEXR_SUCCESS) { + TILEXR_LOG(ERROR) << "direct CCU only init common failed!"; + return TILEXR_ERROR_INTERNAL; + } + ret = InitDirectCcuRuntime(); + if (ret != TILEXR_SUCCESS) { + return ret; + } + inited_ = true; + return TILEXR_SUCCESS; +} + int TileXRComm::InitThread(const std::string &uid) { if (inited_) { @@ -605,6 +1650,10 @@ int TileXRComm::InitThread(const std::string &uid) // UDMA 主要用于跨进程/跨节点通信,线程模式使用进程内共享内存即可 TILEXR_LOG(DEBUG) << "Thread mode: UDMA initialization skipped (single-process multi-thread scenario)"; + ret = InitDirectCcuRuntime(); + if (ret != TILEXR_SUCCESS) { + return ret; + } ret = InitSDMA(); if (ret != TILEXR_SUCCESS) { return ret; @@ -887,6 +1936,9 @@ int TileXRComm::OpenIpcMem(const char names[TILEXR_MAX_RANK_SIZE][IPC_NAME_SIZE] if (ret != RT_ERROR_NONE) { CloseIpcMem(); TILEXR_LOG(ERROR) << "rank : " << rank_ << " localRank : " << localRank_ << " peerMem: " << i << + " devId : " << devId_ << " peerDevId : " << (i < static_cast(devList_.size()) ? devList_[i] : -1) << + " localRankSize : " << localRankSize_ << " ipcNameLen : " << std::strlen(names[i]) << + " ipcNamePrefix : " << std::string(names[i], std::min(std::strlen(names[i]), 16U)) << " IpcOpenMemory err " << ret; return TILEXR_ERROR_INTERNAL; } @@ -963,6 +2015,7 @@ TileXRComm::~TileXRComm() } udmaRegisteredPtr_ = nullptr; udmaInfoDev_ = nullptr; + ResetDirectCcuBasicInfo(); ResetSDMAState(); } diff --git a/src/comm/tilexr_comm.h b/src/comm/tilexr_comm.h index 0f6e37f..279aa92 100644 --- a/src/comm/tilexr_comm.h +++ b/src/comm/tilexr_comm.h @@ -11,6 +11,7 @@ #define TILEXR_COMM_H #include +#include #include #include #include @@ -19,6 +20,9 @@ #include "../include/tilexr_types.h" #include "../include/tilexr_api.h" #include "../include/comm_args.h" +#include "ccu/tilexr_ccu_direct_orchestrator.h" +#include "ccu/tilexr_ccu_direct_runtime.h" +#include "ccu/tilexr_ccu_lower_layer_plan_builder.h" namespace TileXR { constexpr int IPC_NAME_SIZE = 65; @@ -35,6 +39,7 @@ class TileXRComm { TileXRComm(const TileXRComm &) = delete; TileXRComm &operator=(const TileXRComm &) = delete; int Init(); + int InitDirectCcuOnly(); int InitThread(const std::string &uid = "default"); int GetRank() const; int GetRankSize() const; @@ -47,6 +52,44 @@ class TileXRComm { int UnregisterUDMAMemory(TileXRUDMAMemHandle handle); GM_ADDR GetUDMARegistryPtr() const; const TileXRUDMARegistry* GetUDMARegistryHost() const; + int RefreshDirectCcuBasicInfo(uint8_t dieId = 0); + bool HasDirectCcuBasicInfo() const; + int GetDirectCcuBasicInfoStatus() const; + const TileXRCcuBasicInfo *GetDirectCcuBasicInfo() const; + const TileXRCcuDriverAdapterReport &GetDirectCcuBasicInfoReport() const; + int ConfigureDirectCcuLowerLayerTemplate(const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot); + int ConfigureDirectCcuVerifiedEndpointRoutes( + const std::vector &verifiedRoutes); + int ConfigureDirectCcuLocalVerifiedEndpointRoute(const TileXRCcuLowerLayerTransportRoute &route); + int ConfigureDirectCcuLowerLayerTemplateFromAllocation( + const TileXRCcuResourceAllocation &allocation, + const std::vector &remoteCcuBuffers); + int PrepareDirectCcuLowerLayerTemplateFromAllocation(const TileXRCcuResourceAllocation &allocation); + int RefreshDirectCcuLowerLayerPlan(); + bool HasDirectCcuLowerLayerPlan() const; + int GetDirectCcuLowerLayerPlanStatus() const; + const TileXRCcuLowerLayerPlanBuilderReport &GetDirectCcuLowerLayerPlanReport() const; + const TileXRCcuLowerLayerInstallPlan *GetDirectCcuLowerLayerPlan() const; + int ReadDirectCcuInstructionsForDebug( + uint8_t dieId, + uint16_t instructionStartId, + void *instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport *report); + int PrepareDirectCcuInstallAttempt( + const TileXRCcuDirectInstallOptions &options, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); + int PrepareDirectCcuMemoryCopyInstallAttempt( + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuMemoryCopyDirection direction, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); bool IsSDMAAvailable() const; GM_ADDR GetSDMAWorkspacePtr() const; SDMAInitStatus GetSDMAInitStatus() const; @@ -73,9 +116,31 @@ class TileXRComm { int SyncCommArgs(); int InitDumpAddr(); int InitUDMA(); + int InitDirectCcuRuntime(); int InitSDMA(); int UpdateCommArgsDev(); void FreeUDMARegistry(); + void ResetDirectCcuBasicInfo(); + void ResetDirectCcuLowerLayerPlan(); + int FillDirectCcuLowerLayerPlanFromAllocation( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report); + int ExchangeDirectCcuRemoteNotifyCke( + const TileXRCcuResourceAllocation &allocation, + std::vector *remoteCcuBuffers, + TileXRCcuLowerLayerPlanBuilderReport *report); + static int PrepareDirectCcuLowerLayerPlanCallback( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report, + void *userData); + static int DirectCcuAllGatherCallback( + const void *sendBuf, + size_t sendBytes, + void *recvBuf, + void *userData); + int DirectCcuThreadAllGather(const void *sendBuf, size_t sendBytes, void *recvBuf); void ResetSDMAState(); private: @@ -106,6 +171,22 @@ class TileXRComm { GM_ADDR udmaRegisteredPtr_ = nullptr; TileXRUDMARegistry udmaRegistry_ = {}; std::unique_ptr udmaTransport_; + std::unique_ptr ccuDirectRuntime_; + bool directCcuBasicInfoValid_ = false; + int directCcuBasicInfoStatus_ = TILEXR_ERROR_NOT_FOUND; + TileXRCcuBasicInfo directCcuBasicInfo_ = {}; + TileXRCcuDriverAdapterReport directCcuBasicInfoReport_ = {}; + bool directCcuLowerLayerTemplateConfigured_ = false; + bool directCcuLowerLayerPlanValid_ = false; + int directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + TileXRCcuLowerLayerTransportSnapshot directCcuLowerLayerTemplate_ = {}; + TileXRCcuLowerLayerTransportSnapshot directCcuLowerLayerSnapshot_ = {}; + TileXRCcuLowerLayerInstallPlan directCcuLowerLayerPlan_ = {}; + TileXRCcuLowerLayerPlanBuilderReport directCcuLowerLayerPlanReport_ = {}; + std::vector directCcuVerifiedEndpointRoutes_ = {}; + TileXRCcuLowerLayerTransportRoute directCcuLocalVerifiedEndpointRoute_ = {}; + bool directCcuLocalVerifiedEndpointRouteValid_ = false; + uint64_t directCcuThreadAllGatherRound_ = 0; GM_ADDR sdmaWorkspaceDev_ = nullptr; SDMAInitStatus sdmaInitStatus_ = SDMAInitStatus::DISABLED_BY_ENV; std::unique_ptr sdmaTransport_; diff --git a/src/comm/tilexr_internal.cpp b/src/comm/tilexr_internal.cpp index e39bd33..3f53f24 100644 --- a/src/comm/tilexr_internal.cpp +++ b/src/comm/tilexr_internal.cpp @@ -45,6 +45,25 @@ const std::unordered_map CHIP_MAP = { {"Ascend950PR_9599", ChipName::CHIP_950PR} }; +const std::unordered_map CHIP_PREFIX_MAP = { + {"Ascend950DT_", ChipName::CHIP_950}, + {"Ascend950PR_", ChipName::CHIP_950PR} +}; + +ChipName ResolveChipNameFromSocVersion(const std::string& chipName) +{ + auto it = CHIP_MAP.find(chipName); + if (it != CHIP_MAP.end()) { + return it->second; + } + for (const auto& item : CHIP_PREFIX_MAP) { + if (chipName.find(item.first) == 0) { + return item.second; + } + } + return ChipName::RESERVED; +} + /** * @brief 用于获取芯片名称 */ @@ -65,12 +84,8 @@ ChipName GetChipName() string chipName(ver); TILEXR_LOG(DEBUG) << "rtGetSocVersion -- The result after converting ver to string is:" << chipName; - auto it = CHIP_MAP.find(chipName); - if (it != CHIP_MAP.end()) { - curChipName = it->second; - } else if (chipName.find("Ascend950PR_") == 0) { - curChipName = ChipName::CHIP_950PR; - } else { + curChipName = ResolveChipNameFromSocVersion(chipName); + if (curChipName == ChipName::RESERVED) { TILEXR_LOG(WARN) << "There is no commitment to the supported chip types yet," << " and it is not certain whether the functions will work properly."; } diff --git a/src/include/tilexr_api.h b/src/include/tilexr_api.h index 7b4fd3a..d71faf8 100644 --- a/src/include/tilexr_api.h +++ b/src/include/tilexr_api.h @@ -10,19 +10,123 @@ #ifndef TILEXR_API_H #define TILEXR_API_H +#ifdef __cplusplus #include -#include +#include #include "comm_args.h" #include "tilexr_udma_reg.h" +#else +#include +#include +#include +typedef uint8_t *GM_ADDR; +#endif + #ifdef __cplusplus extern "C" { #endif // __cplusplus typedef void *TileXRCommPtr; +typedef void *TileXRDirectCcuPreparedTasksPtr; typedef uint32_t TileXRUDMAMemHandle; #define TILEXRUNIQUE_ID_BYTES 128 +#define TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES 2048 +#define TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_MISSION 0U +#define TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_FULL_REPOSITORY 1U +#define TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_INSTRUCTION_BYTES 0U +#define TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_DESCRIPTOR_BYTES 1U +#define TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL 0U +#define TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL_MODULE3 1U +#define TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_RT_HBM 2U +#define TILEXR_DIRECT_CCU_INSTALL_ORDER_REPOSITORY_FIRST 0U +#define TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST 1U +#define TILEXR_DIRECT_CCU_MEMORY_COPY_REMOTE_TO_LOCAL 0U +#define TILEXR_DIRECT_CCU_MEMORY_COPY_LOCAL_TO_REMOTE 1U +#define TILEXR_DIRECT_CCU_SQE_ARGS_LEN 13U typedef struct { char internal[TILEXRUNIQUE_ID_BYTES]; } TileXRUniqueId; +typedef struct TileXRDirectCcuPrepareOptions { + uint32_t syncResourceCount; + /* 0 is a valid explicit no-SQE-load mode; use TILEXR_DIRECT_CCU_SQE_ARGS_LEN for full SQE args. */ + uint32_t sqeArgCount; + uint32_t syncInstructionCount; + uint32_t bindingsPerSyncResource; + uint16_t missionStartId; + uint16_t instructionStartId; + uint16_t missionInstructionStartId; + uint16_t xnStartId; + uint16_t gsaStartId; + uint16_t remoteXnStartId; + uint16_t remoteXnCount; + uint16_t ckeStartId; + uint16_t channelStartId; + uint16_t localWaitCkeStartId; + uint16_t localWaitCkeCount; + uint16_t remoteNotifyCkeStartId; + uint16_t remoteNotifyCkeCount; + uint32_t repositoryInstallWindow; + uint32_t repositoryInstallDataLenMode; + uint32_t repositoryMemoryAllocMode; + uint32_t installOrder; + uint32_t deviceId; + uint32_t rank; + const char *provider; +} TileXRDirectCcuPrepareOptions; + +typedef struct TileXRDirectCcuMemoryCopyPrepareOptions { + TileXRDirectCcuPrepareOptions prepare; + uint64_t localSourceAddr; + uint64_t localDestinationAddr; + uint64_t bytes; + uint32_t peerRank; + uint32_t direction; +} TileXRDirectCcuMemoryCopyPrepareOptions; + +typedef struct TileXRDirectCcuPrepareReport { + bool pipelineBuilt; + bool installAttempted; + bool installSucceeded; + bool submitReady; + uint32_t requiredInstallSurfaceCount; + uint32_t publicVerifiedInstallSurfaceCount; + uint32_t missingInstallSurfaceCount; + uint32_t taskCount; + uint32_t submitTaskCount; + char message[TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES]; +} TileXRDirectCcuPrepareReport; + +typedef struct TileXRDirectCcuSubmitReport { + bool submitted; + uint32_t taskCount; + uint32_t submittedTaskCount; + char message[TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES]; +} TileXRDirectCcuSubmitReport; + +typedef struct TileXRDirectCcuTaskInfo { + uint8_t dieId; + uint8_t missionId; + uint16_t timeout; + uint16_t instStartId; + uint16_t instCnt; + uint32_t key; + uint32_t argSize; + uint64_t args[TILEXR_DIRECT_CCU_SQE_ARGS_LEN]; +} TileXRDirectCcuTaskInfo; + +#define TILEXR_DIRECT_CCU_INSTRUCTION_WORDS 4U +typedef struct TileXRDirectCcuInstructionWords { + uint64_t words[TILEXR_DIRECT_CCU_INSTRUCTION_WORDS]; +} TileXRDirectCcuInstructionWords; + +typedef struct TileXRDirectCcuInstructionReadbackReport { + bool readbackAttempted; + uint32_t readInstructionCount; + uint32_t opcode; + int driverRet; + int opRet; + char message[TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES]; +} TileXRDirectCcuInstructionReadbackReport; + int TileXRGetUniqueId(TileXRUniqueId *uniqueId, int commDomain); int TileXRCommInitRankLocal(int rankSize, int rank, TileXRCommPtr *comm); @@ -33,9 +137,13 @@ int TileXRCommInitRankWithCustDomainSize(int commDomain, int bufferSize, int ran int TileXRCommInitRankWithDomain(int commDomain, int rankSize, int rank, TileXRCommPtr *comm); +int TileXRCommInitRankDirectCcuWithDomain(int commDomain, int rankSize, int rank, TileXRCommPtr *comm); + +#ifdef __cplusplus int TileXRGetCommArgsDev(TileXRCommPtr comm, GM_ADDR &commArgsPtr); int TileXRGetCommArgsHost(TileXRCommPtr comm, TileXR::CommArgs *&commArgsPtr); +#endif int TileXRCommNextMagic(TileXRCommPtr comm, int64_t *magic); @@ -43,14 +151,54 @@ int TileXRUDMARegister(TileXRCommPtr comm, GM_ADDR localPtr, size_t bytes, TileX int TileXRUDMAUnregister(TileXRCommPtr comm, TileXRUDMAMemHandle handle); +#ifdef __cplusplus int TileXRGetUDMARegistryDev(TileXRCommPtr comm, GM_ADDR ®istryPtr); int TileXRGetUDMARegistryHost(TileXRCommPtr comm, const TileXR::TileXRUDMARegistry **registry); +#endif int TileXRSDMAAvailable(TileXRCommPtr comm, bool *available); int TileXRGetSDMAWorkspaceDev(TileXRCommPtr comm, GM_ADDR *workspace); +int TileXRCommPrepareDirectCcu( + TileXRCommPtr comm, + const TileXRDirectCcuPrepareOptions *options, + TileXRDirectCcuPreparedTasksPtr *prepared, + TileXRDirectCcuPrepareReport *report); + +int TileXRCommPrepareDirectCcuMemoryCopy( + TileXRCommPtr comm, + const TileXRDirectCcuMemoryCopyPrepareOptions *options, + TileXRDirectCcuPreparedTasksPtr *prepared, + TileXRDirectCcuPrepareReport *report); + +int TileXRDirectCcuGetPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t index, + TileXRDirectCcuTaskInfo *task); + +int TileXRDirectCcuSubmitPrepared( + TileXRDirectCcuPreparedTasksPtr prepared, + void *stream, + TileXRDirectCcuSubmitReport *report); + +int TileXRDirectCcuSubmitPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t index, + void *stream, + TileXRDirectCcuSubmitReport *report); + +int TileXRCommReadDirectCcuInstructions( + TileXRCommPtr comm, + uint8_t dieId, + uint16_t instructionStartId, + uint32_t instructionCount, + TileXRDirectCcuInstructionWords *instructions, + TileXRDirectCcuInstructionReadbackReport *report); + +int TileXRDirectCcuDestroyPrepared(TileXRDirectCcuPreparedTasksPtr prepared); + void TileXRPrintDFX2Log(TileXRCommPtr comm); int TileXRCommInit(int rank, int rankSize, TileXRCommPtr *comms); diff --git a/tests/ccu/CMakeLists.txt b/tests/ccu/CMakeLists.txt new file mode 100644 index 0000000..212090f --- /dev/null +++ b/tests/ccu/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# Copyright (c) 2026 TileXR Project +# +# The direct CCU smoke runner compiles its probe against the active TileXR build +# so that it can test the exact libtile-comm.so under validation. Keep this +# directory free of private producer probe targets. +# + +cmake_minimum_required(VERSION 3.16) +project(TileXR_CCU_Tests LANGUAGES CXX) diff --git a/tests/ccu/ccu_lower_layer_payload_hcomm_oracle.cpp b/tests/ccu/ccu_lower_layer_payload_hcomm_oracle.cpp new file mode 100644 index 0000000..8659f4f --- /dev/null +++ b/tests/ccu/ccu_lower_layer_payload_hcomm_oracle.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2026 TileXR Project + * + * Offline byte-level oracle for TileXR CCU lower-layer payloads. + * + * Reference hcomm files mirrored here as packed test-only structs: + * - ccu_pfe/ccu_pfe_mgr.h + * - ccu_pfe/ccu_pfe_mgr.cc + * - ccu_jetty_ctx_mgr.h + * - ccu_jetty_ctx_mgr.cc + * - ccu_channel_ctx_v1/ccu_channel_ctx_mgr_v1.h + * - ccu_channel_ctx_v1/ccu_channel_ctx_mgr_v1.cc + * + * This probe intentionally does not include hcomm/HCCL headers. It is a + * second implementation of the hcomm packing formulas, used to compare the + * bytes generated by TileXR's direct-CCU payload packers. + */ + +#include "ccu/tilexr_ccu_lower_layer_payloads.h" + +#include +#include +#include +#include +#include + +using namespace TileXR; + +namespace { + +constexpr uint8_t HCOMM_DB_ADDR_TYPE = 1; +constexpr uint8_t HCOMM_TOKEN_VALUE_IS_VALID = 1; +constexpr uint16_t HCOMM_TOKEN_VALUE_VALID = 1; +constexpr uint16_t HCOMM_REMOTE_CCU_VA_RIGHT_SHIFT_NUM = 23; +constexpr uint32_t HCOMM_CCU_WQE_NUM_PER_SQE = 4; + +#pragma pack(push, 1) +struct HcommPfeCtx { + uint16_t startJettyId; + uint16_t jettyNum : 7; + uint16_t startLocalJettyCtxId : 7; + uint16_t rsvBit : 2; + uint16_t rsv[2]; +}; + +struct HcommLocalJettyCtxData { + uint16_t doorbellAddr[4]; + uint16_t pfeIdx : 4; + uint16_t ioDieId : 1; + uint16_t doorbellAddrType : 1; + uint16_t tokenValueIsValid : 1; + uint16_t cqeErrValue : 1; + uint16_t tokenIdLow : 8; + uint16_t tokenIdHigh : 12; + uint16_t tokenValueLow : 4; + uint16_t tokenValueMiddle; + uint16_t tokenValueHigh : 12; + uint16_t sqeBasicBlockLeftShifts : 4; + uint16_t pi; + uint16_t ci; + uint16_t maxCi; + uint16_t oooCqeCnt : 12; + uint16_t startWqeBasicBlockIdxLow : 4; + uint16_t startWqeBasicBlockIdxHigh : 8; + uint16_t doorbellSendState : 2; + uint16_t rsvSixBits : 6; + uint16_t rsvs[3]; +}; + +struct HcommChannelCtxDataV1 { + uint8_t eidRaw[TILEXR_CCU_EID_BYTES]; + uint16_t vtpLow; + uint16_t vtpHigh : 8; + uint16_t srcPfeId : 4; + uint16_t startJettyIdLow : 4; + uint16_t startJettyIdHigh : 12; + uint16_t jettyNumLow : 4; + uint16_t jettyNumHigh : 3; + uint16_t ioDieId : 1; + uint16_t dstTokenIdLow : 12; + uint16_t dstTokenIdHigh : 8; + uint16_t dstTokenValueLow : 8; + uint16_t dstTokenValueMiddle; + uint16_t dstTokenValueHigh : 8; + uint16_t dstVaLow : 8; + uint16_t dstVaMiddle; + uint16_t dstVaHigh; + uint16_t dstVaHigher : 1; + uint16_t dstTokenValueValid : 1; + uint16_t rsv14Bits : 14; + uint16_t rsvs[14]; +}; +#pragma pack(pop) + +static_assert(sizeof(HcommPfeCtx) == TILEXR_CCU_PFE_CTX_BYTES, "hcomm PFE ctx size mismatch"); +static_assert(sizeof(HcommLocalJettyCtxData) == TILEXR_CCU_LOCAL_JETTY_CTX_BYTES, + "hcomm local jetty ctx size mismatch"); +static_assert(sizeof(HcommChannelCtxDataV1) == TILEXR_CCU_CHANNEL_CTX_V1_BYTES, + "hcomm channel ctx v1 size mismatch"); + +uint16_t Log2PowerOfTwo(uint32_t value) +{ + uint16_t log2 = 0; + while (value > 1U) { + value >>= 1U; + ++log2; + } + return log2; +} + +HcommPfeCtx BuildHcommPfeCtx( + uint16_t startTaJettyId, + uint16_t jettyCount, + uint16_t startLocalJettyCtxId) +{ + HcommPfeCtx ctx {}; + ctx.startJettyId = startTaJettyId; + ctx.jettyNum = static_cast(jettyCount - 1U); + ctx.startLocalJettyCtxId = startLocalJettyCtxId; + return ctx; +} + +HcommLocalJettyCtxData BuildHcommLocalJettyCtx( + uint8_t dieId, + uint32_t pfeId, + uint64_t doorbellVa, + uint32_t doorbellTokenId, + uint32_t doorbellTokenValue, + uint32_t sqDepth, + uint16_t wqeBasicBlockStartId) +{ + HcommLocalJettyCtxData data {}; + uint16_t doorbell[4] {}; + std::memcpy(doorbell, &doorbellVa, sizeof(doorbell)); + data.doorbellAddr[0] = doorbell[0]; + data.doorbellAddr[1] = doorbell[1]; + data.doorbellAddr[2] = doorbell[2]; + data.doorbellAddr[3] = doorbell[3]; + data.pfeIdx = static_cast(pfeId); + data.ioDieId = dieId; + data.doorbellAddrType = HCOMM_DB_ADDR_TYPE; + data.tokenValueIsValid = HCOMM_TOKEN_VALUE_IS_VALID; + data.tokenIdLow = doorbellTokenId & 0x000000ffU; + data.tokenIdHigh = (doorbellTokenId >> 8U) & 0x00000fffU; + data.tokenValueLow = doorbellTokenValue & 0x0000000fU; + data.tokenValueMiddle = (doorbellTokenValue >> 4U) & 0x0000ffffU; + data.tokenValueHigh = (doorbellTokenValue >> 20U) & 0x00000fffU; + data.sqeBasicBlockLeftShifts = Log2PowerOfTwo(sqDepth * HCOMM_CCU_WQE_NUM_PER_SQE); + data.startWqeBasicBlockIdxLow = wqeBasicBlockStartId & 0x0000000fU; + data.startWqeBasicBlockIdxHigh = (wqeBasicBlockStartId >> 4U) & 0x000000ffU; + return data; +} + +HcommChannelCtxDataV1 BuildHcommChannelCtxV1( + const std::array& remoteEid, + uint32_t tpn, + uint32_t feId, + uint16_t startTaJettyId, + uint16_t jettyCount, + uint8_t dieId, + uint32_t memTokenId, + uint32_t memTokenValue, + uint64_t remoteCcuVa) +{ + HcommChannelCtxDataV1 data {}; + std::copy(remoteEid.begin(), remoteEid.end(), data.eidRaw); + data.vtpLow = tpn & 0x0000ffffU; + data.vtpHigh = ((tpn & 0xffff0000U) >> 16U) & 0x000000ffU; + data.srcPfeId = static_cast(feId); + data.startJettyIdLow = startTaJettyId & 0x000fU; + data.startJettyIdHigh = (startTaJettyId >> 4U) & 0x0fffU; + const uint8_t jettyNum = static_cast(jettyCount - 1U); + data.jettyNumLow = jettyNum & 0x000fU; + data.jettyNumHigh = (jettyNum >> 4U) & 0x0007U; + data.ioDieId = dieId; + data.dstTokenIdLow = memTokenId & 0x00000fffU; + data.dstTokenIdHigh = (memTokenId >> 12U) & 0x000000ffU; + data.dstTokenValueLow = memTokenValue & 0x000000ffU; + data.dstTokenValueMiddle = (memTokenValue >> 8U) & 0x0000ffffU; + data.dstTokenValueHigh = (memTokenValue >> 24U) & 0x000000ffU; + const uint64_t dstVa = remoteCcuVa >> HCOMM_REMOTE_CCU_VA_RIGHT_SHIFT_NUM; + data.dstVaLow = dstVa & 0x00000000000000ffULL; + data.dstVaMiddle = (dstVa >> 8U) & 0x000000000000ffffULL; + data.dstVaHigh = (dstVa >> 24U) & 0x000000000000ffffULL; + data.dstVaHigher = (dstVa >> 40U) & 0x0000000000000001ULL; + data.dstTokenValueValid = HCOMM_TOKEN_VALUE_VALID; + return data; +} + +template +bool RawEquals(const HcommStruct& expected, const TileXRStruct& actual) +{ + return sizeof(expected) == sizeof(actual.raw) && + std::memcmp(&expected, actual.raw, sizeof(expected)) == 0; +} + +template +int CheckRawEquals(const char* name, const HcommStruct& expected, const TileXRStruct& actual) +{ + if (RawEquals(expected, actual)) { + return 0; + } + const auto* expectedBytes = reinterpret_cast(&expected); + for (uint32_t i = 0; i < sizeof(expected); ++i) { + if (expectedBytes[i] != actual.raw[i]) { + std::cerr << name << " mismatch at byte " << i + << " expected=0x" << std::hex << static_cast(expectedBytes[i]) + << " actual=0x" << static_cast(actual.raw[i]) + << std::dec << "\n"; + return 1; + } + } + std::cerr << name << " mismatch\n"; + return 1; +} + +int CheckPfe() +{ + TileXRCcuPfeCtx actual; + TileXRCcuLowerLayerPayloadReport report; + TileXRCcuPfeCtxSpec spec; + spec.startJettyId = 0x1234; + spec.jettyCount = 5; + spec.startLocalJettyCtxId = 0x22; + if (TileXRCcuBuildPfeCtx(spec, &actual, &report) != TILEXR_SUCCESS) { + std::cerr << "TileXR PFE build failed: " << report.message << "\n"; + return 1; + } + return CheckRawEquals("PFE", BuildHcommPfeCtx(0x1234, 5, 0x22), actual); +} + +int CheckJetty() +{ + TileXRCcuLocalJettyCtxData actual; + TileXRCcuLowerLayerPayloadReport report; + TileXRCcuLocalJettyCtxSpec spec; + spec.dieId = 1; + spec.pfeId = 3; + spec.doorbellVa = 0x1122334455667788ULL; + spec.doorbellTokenId = 0x000abcdeU; + spec.doorbellTokenValue = 0x89abcdefU; + spec.sqDepth = 16; + spec.wqeBasicBlockStartId = 0x9a; + if (TileXRCcuBuildLocalJettyCtx(spec, &actual, &report) != TILEXR_SUCCESS) { + std::cerr << "TileXR jetty build failed: " << report.message << "\n"; + return 1; + } + return CheckRawEquals( + "Jetty", + BuildHcommLocalJettyCtx(1, 3, 0x1122334455667788ULL, 0x000abcdeU, 0x89abcdefU, 16, 0x9a), + actual); +} + +int CheckChannel() +{ + std::array remoteEid {}; + for (uint32_t i = 0; i < remoteEid.size(); ++i) { + remoteEid[i] = static_cast(0x10 + i); + } + + TileXRCcuChannelCtxDataV1 actual; + TileXRCcuLowerLayerPayloadReport report; + TileXRCcuChannelCtxV1Spec spec; + spec.remoteEid = remoteEid; + spec.tpn = 0x00ab5678U; + spec.sourcePfeId = 5; + spec.startJettyId = 0x0234; + spec.jettyCount = 7; + spec.dieId = 1; + spec.memoryTokenId = 0x000abcdeU; + spec.memoryTokenValue = 0x89abcdefU; + spec.remoteCcuVa = 0x000123456789ab00ULL; + if (TileXRCcuBuildChannelCtxV1(spec, &actual, &report) != TILEXR_SUCCESS) { + std::cerr << "TileXR channel build failed: " << report.message << "\n"; + return 1; + } + return CheckRawEquals( + "Channel", + BuildHcommChannelCtxV1( + remoteEid, + 0x00ab5678U, + 5, + 0x0234, + 7, + 1, + 0x000abcdeU, + 0x89abcdefU, + 0x000123456789ab00ULL), + actual); +} + +int CheckZeroTokenValues() +{ + TileXRCcuLowerLayerPayloadReport report; + TileXRCcuLocalJettyCtxData actualJetty; + TileXRCcuLocalJettyCtxSpec jettySpec; + jettySpec.dieId = 0; + jettySpec.pfeId = 2; + jettySpec.doorbellVa = 0x1020304050607080ULL; + jettySpec.doorbellTokenId = 0x12345U; + jettySpec.doorbellTokenValue = 0; + jettySpec.sqDepth = 8; + if (TileXRCcuBuildLocalJettyCtx(jettySpec, &actualJetty, &report) != TILEXR_SUCCESS) { + std::cerr << "TileXR zero-token jetty build failed: " << report.message << "\n"; + return 1; + } + if (CheckRawEquals( + "ZeroTokenJetty", + BuildHcommLocalJettyCtx(0, 2, 0x1020304050607080ULL, 0x12345U, 0, 8, 0), + actualJetty) != 0) { + return 1; + } + + std::array remoteEid {}; + for (uint32_t i = 0; i < remoteEid.size(); ++i) { + remoteEid[i] = static_cast(0xa0 + i); + } + TileXRCcuChannelCtxDataV1 actualChannel; + TileXRCcuChannelCtxV1Spec channelSpec; + channelSpec.remoteEid = remoteEid; + channelSpec.tpn = 0x13579U; + channelSpec.sourcePfeId = 2; + channelSpec.startJettyId = 0x44; + channelSpec.jettyCount = 1; + channelSpec.dieId = 0; + channelSpec.memoryTokenId = 0x12345U; + channelSpec.memoryTokenValue = 0; + channelSpec.remoteCcuVa = 0x0000001234000000ULL; + if (TileXRCcuBuildChannelCtxV1(channelSpec, &actualChannel, &report) != TILEXR_SUCCESS) { + std::cerr << "TileXR zero-token channel build failed: " << report.message << "\n"; + return 1; + } + return CheckRawEquals( + "ZeroTokenChannel", + BuildHcommChannelCtxV1(remoteEid, 0x13579U, 2, 0x44, 1, 0, 0x12345U, 0, 0x0000001234000000ULL), + actualChannel); +} + +} // namespace + +int main() +{ + if (CheckPfe() != 0 || CheckJetty() != 0 || CheckChannel() != 0 || CheckZeroTokenValues() != 0) { + return 1; + } + std::cout << "hcomm lower-layer payload oracle matched" << std::endl; + return 0; +} diff --git a/tests/ccu/ccu_npu_smi_busy_guard.py b/tests/ccu/ccu_npu_smi_busy_guard.py new file mode 100644 index 0000000..a8f103e --- /dev/null +++ b/tests/ccu/ccu_npu_smi_busy_guard.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import argparse +import re +import sys +from pathlib import Path + + +def parse_devices(text): + devices = set() + for item in text.split(","): + item = item.strip() + if not item: + continue + devices.add(int(item, 0)) + return devices + + +def first_int(text): + match = re.search(r"\d+", text) + return int(match.group(0), 10) if match else None + + +def parse_busy_processes(log_text, selected_devices): + busy = [] + in_process_table = False + for line in log_text.splitlines(): + if "|" not in line: + continue + lower_line = line.lower() + if "process id" in lower_line or "process name" in lower_line: + in_process_table = True + continue + if not in_process_table: + continue + fields = [field.strip() for field in line.strip().strip("|").split("|")] + if len(fields) < 3: + continue + device = first_int(fields[0]) + if device is None or device not in selected_devices: + continue + + pid = first_int(fields[1]) + process_index = 2 + if (pid is None or pid == 0) and len(fields) >= 4: + pid = first_int(fields[2]) + process_index = 3 + if pid is None or pid == 0: + continue + process = fields[process_index].split()[0] if fields[process_index].split() else "unknown" + if process in {"-", "N/A", "NA"}: + continue + busy.append((device, pid, process)) + return busy + + +def parse_unhealthy_devices(log_text, selected_devices): + unhealthy = [] + observed_selected = set() + in_status_table = False + for line in log_text.splitlines(): + if "|" not in line: + continue + lower_line = line.lower() + if "process id" in lower_line or "process name" in lower_line: + in_status_table = False + continue + if "health" in lower_line: + in_status_table = True + continue + if not in_status_table: + continue + + fields = [field.strip() for field in line.strip().strip("|").split("|")] + if len(fields) < 3: + continue + device = first_int(fields[0]) + if device is None or device not in selected_devices: + continue + health = fields[2].split()[0] if fields[2].split() else "" + if not health: + continue + observed_selected.add(device) + if health.upper() != "OK": + unhealthy.append((device, health)) + return unhealthy, observed_selected + + +def main(): + parser = argparse.ArgumentParser(description="Reject TileXR CCU smoke runs on busy or unhealthy selected NPUs.") + parser.add_argument("--log", required=True, help="npu-smi info log path") + parser.add_argument("--devices", required=True, help="comma-separated NPU device ids selected for the smoke") + parser.add_argument( + "--allow-unhealthy", + action="store_true", + help="Allow selected devices with non-OK health while still rejecting busy processes.", + ) + args = parser.parse_args() + + try: + selected_devices = parse_devices(args.devices) + except ValueError as exc: + print(f"invalid device list {args.devices!r}: {exc}", file=sys.stderr) + return 2 + if not selected_devices: + print("no selected NPU devices to check", file=sys.stderr) + return 2 + + log_path = Path(args.log) + log_text = log_path.read_text(encoding="utf-8", errors="replace") + unhealthy, observed_health = parse_unhealthy_devices(log_text, selected_devices) + if unhealthy: + if not args.allow_unhealthy: + for device, health in unhealthy: + print(f"unhealthy selected NPU device={device} health={health}") + return 1 + for device, health in unhealthy: + print(f"unhealthy selected NPU devices allowed device={device} health={health}") + if observed_health and not unhealthy: + print(f"selected NPU devices healthy devices={args.devices}") + + busy = parse_busy_processes(log_text, selected_devices) + if busy: + for device, pid, process in busy: + print(f"busy selected NPU process device={device} pid={pid} process={process}") + return 1 + + print(f"no selected NPU processes devices={args.devices}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/ccu/ccu_public_direct_api_compile_probe.c b/tests/ccu/ccu_public_direct_api_compile_probe.c new file mode 100644 index 0000000..fb86649 --- /dev/null +++ b/tests/ccu/ccu_public_direct_api_compile_probe.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026 TileXR Project + * + * External-user compile probe for the public direct CCU API. + * + * This file intentionally includes only tilexr_api.h from TileXR. It is + * compiled to an object file, not linked, so it verifies public declarations + * without requiring Ascend runtime or NPU hardware. + */ + +#include "tilexr_api.h" + +#ifndef TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES +#error "TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES must be public" +#endif + +int TileXRPublicDirectCcuApiCompileProbe(void) +{ + TileXRCommPtr comm = 0; + TileXRDirectCcuPreparedTasksPtr prepared = 0; + TileXRDirectCcuPrepareOptions options = {0}; + TileXRDirectCcuPrepareReport prepareReport = {0}; + TileXRDirectCcuSubmitReport submitReport = {0}; + TileXRDirectCcuTaskInfo task = {0}; + + int (*prepareFn)( + TileXRCommPtr, + const TileXRDirectCcuPrepareOptions *, + TileXRDirectCcuPreparedTasksPtr *, + TileXRDirectCcuPrepareReport *) = &TileXRCommPrepareDirectCcu; + int (*getTaskFn)( + TileXRDirectCcuPreparedTasksPtr, + uint32_t, + TileXRDirectCcuTaskInfo *) = &TileXRDirectCcuGetPreparedTask; + int (*submitFn)( + TileXRDirectCcuPreparedTasksPtr, + void *, + TileXRDirectCcuSubmitReport *) = &TileXRDirectCcuSubmitPrepared; + int (*destroyFn)(TileXRDirectCcuPreparedTasksPtr) = &TileXRDirectCcuDestroyPrepared; + + options.syncResourceCount = 1; + options.syncInstructionCount = 2; + options.bindingsPerSyncResource = 1; + options.gsaStartId = 510; + options.provider = "tilexr-public-direct-ccu-compile-probe"; + + prepareReport.message[0] = '\0'; + submitReport.message[0] = '\0'; + task.args[0] = 0; + + (void)comm; + (void)prepared; + (void)prepareFn; + (void)getTaskFn; + (void)submitFn; + (void)destroyFn; + return (int)(options.syncResourceCount + options.gsaStartId + prepareReport.taskCount + + submitReport.taskCount + task.argSize); +} diff --git a/tests/ccu/ccu_tilexr_basic_info_probe.cpp b/tests/ccu/ccu_tilexr_basic_info_probe.cpp new file mode 100644 index 0000000..73e673f --- /dev/null +++ b/tests/ccu/ccu_tilexr_basic_info_probe.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + * + * Probe TileXR-owned direct CCU runtime basic-info path. + * This does not install repository state and does not submit CCU tasks. + */ + +#include "ccu/tilexr_ccu_direct_runtime.h" +#include "ccu/tilexr_ccu_specs.h" + +#include +#include +#include +#include + +namespace { + +uint32_t ParseUintArg(const char* value, uint32_t fallback) +{ + if (value == nullptr) { + return fallback; + } + char* end = nullptr; + const unsigned long parsed = std::strtoul(value, &end, 0); + if (end == value || *end != '\0') { + return fallback; + } + return static_cast(parsed); +} + +void PrintHex32(const char* label, uint32_t value) +{ + std::cout << label << "=0x" << std::hex << std::setw(8) << std::setfill('0') << value << std::dec; +} + +void PrintHex64(const char* label, uint64_t value) +{ + std::cout << label << "=0x" << std::hex << std::setw(16) << std::setfill('0') << value << std::dec; +} + +void PrintBasicInfo( + uint32_t deviceLogicId, + uint8_t dieId, + const TileXR::TileXRCcuBasicInfo& basicInfo, + const TileXR::TileXRCcuDriverAdapterReport& adapterReport, + const TileXR::TileXRCcuSpecInfo& specInfo) +{ + std::cout << "tilexr_ccu_basic_info result" + << " deviceLogicId=" << deviceLogicId + << " devicePhyId=" << adapterReport.devicePhyId + << " dieId=" << static_cast(dieId) + << " driverRet=" << adapterReport.driverRet + << " opRet=" << adapterReport.opRet + << " msId=" << basicInfo.msId + << " tokenId=" << basicInfo.msidToken.tokenId + << " tokenValue=" << basicInfo.msidToken.tokenValue + << " tokenValid=" << (basicInfo.msidToken.valid ? 1 : 0) + << " "; + PrintHex32("missionKey", basicInfo.missionKey); + std::cout << " "; + PrintHex64("resourceAddr", basicInfo.resourceAddr); + std::cout << " instructionNum=" << specInfo.instructionNum + << " xnNum=" << specInfo.xnNum + << " ckeNum=" << specInfo.ckeNum + << " channelNum=" << specInfo.channelNum + << " missionNum=" << specInfo.missionNum + << std::endl; +} + +} // namespace + +int main(int argc, char** argv) +{ + const uint32_t deviceLogicId = ParseUintArg(argc > 1 ? argv[1] : nullptr, 0); + const uint8_t dieId = static_cast(ParseUintArg(argc > 2 ? argv[2] : nullptr, 0)); + + TileXR::TileXRCcuDirectRuntime runtime; + TileXR::TileXRCcuDirectRuntimeOptions options; + options.devId = static_cast(deviceLogicId); + options.rank = 0; + options.rankSize = 1; + + TileXR::TileXRCcuDirectRuntimeReport runtimeReport; + int ret = runtime.Init(options, &runtimeReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << "failed to initialize TileXR CCU direct runtime: " + << runtimeReport.message + << " deviceLogicId=" << runtimeReport.logicDevId + << " devicePhyId=" << runtimeReport.devicePhyId + << " hdcType=" << runtimeReport.hdcType + << " raInitialized=" << (runtimeReport.raInitialized ? 1 : 0) + << std::endl; + return 2; + } + std::cout << "tilexr_ccu_basic_info runtime" + << " deviceLogicId=" << runtimeReport.logicDevId + << " devicePhyId=" << runtimeReport.devicePhyId + << " hdcType=" << runtimeReport.hdcType + << " raInitialized=" << (runtimeReport.raInitialized ? 1 : 0) + << " message=\"" << runtimeReport.message << "\"" + << std::endl; + + TileXR::TileXRCcuBasicInfo basicInfo; + TileXR::TileXRCcuDriverAdapterReport adapterReport; + ret = runtime.QueryBasicInfo(dieId, &basicInfo, &adapterReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << "failed to query CCU basic info: " << adapterReport.message + << " driverRet=" << adapterReport.driverRet + << " opRet=" << adapterReport.opRet << std::endl; + return 3; + } + + TileXR::TileXRCcuSpecInfo specInfo; + TileXR::TileXRCcuSpecsReport specsReport; + ret = TileXR::TileXRCcuDecodeBasicInfo(basicInfo, &specInfo, &specsReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << "failed to decode CCU basic info: " << specsReport.message << std::endl; + return 4; + } + + PrintBasicInfo(deviceLogicId, dieId, basicInfo, adapterReport, specInfo); + return 0; +} diff --git a/tests/ccu/ccu_tilexr_direct_smoke_probe.cpp b/tests/ccu/ccu_tilexr_direct_smoke_probe.cpp new file mode 100644 index 0000000..232d46d --- /dev/null +++ b/tests/ccu/ccu_tilexr_direct_smoke_probe.cpp @@ -0,0 +1,1261 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + * + * Private C++ integration probe for the TileXR-owned direct CCU prepare path. + * The default run is intentionally hardware-safe. Set TILEXR_CCU_DIRECT_SMOKE_ENABLE=1 + * in a real multi-rank TileXRComm launch to prepare the no-hcomm direct CCU install attempt. + */ + +#include "acl/acl.h" +#include "acl/acl_rt.h" +#include "tilexr_api.h" +#include "tilexr_types.h" +#include "runtime/dev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr const char* kEnableEnv = "TILEXR_CCU_DIRECT_SMOKE_ENABLE"; +constexpr const char* kThreadModeEnv = "TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE"; +constexpr const char* kDirectCcuOnlyInitEnv = "TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT"; +constexpr const char* kFastExitOnPrepareFailureEnv = "TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE"; +constexpr const char* kFastExitAfterRunEnv = "TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN"; +constexpr const char* kTraceLifecycleEnv = "TILEXR_CCU_DIRECT_SMOKE_TRACE_LIFECYCLE"; +constexpr const char* kReadbackInstructionsEnv = "TILEXR_CCU_DIRECT_SMOKE_READBACK_INSTRUCTIONS"; +constexpr const char* kSubmitEnv = "TILEXR_CCU_DIRECT_SMOKE_SUBMIT"; +constexpr const char* kReadyDirEnv = "TILEXR_CCU_DIRECT_SMOKE_READY_DIR"; +constexpr const char* kDoneDirEnv = "TILEXR_CCU_DIRECT_SMOKE_DONE_DIR"; +constexpr const char* kReadyTimeoutMsEnv = "TILEXR_CCU_DIRECT_SMOKE_READY_TIMEOUT_MS"; +constexpr const char* kSubmitTaskSelectorEnv = "TILEXR_CCU_DIRECT_SMOKE_SUBMIT_TASK_SELECTOR"; +constexpr const char* kDelayRankEnv = "TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK"; +constexpr const char* kPreSubmitDelayMsEnv = "TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS"; +constexpr const char* kP2pCcuCopyEnv = "TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY"; +constexpr const char* kExpectP2pCcuCopyEnv = "TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY"; +constexpr const char* kP2pCcuCopyBytesEnv = "TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES"; +constexpr const char* kLocalWaitCkeStartEnv = "TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START"; +constexpr const char* kLocalWaitCkeCountEnv = "TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT"; +constexpr const char* kRemoteNotifyCkeStartEnv = "TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START"; +constexpr const char* kRemoteNotifyCkeCountEnv = "TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT"; +constexpr const char* kBarrierModeEnv = "TILEXR_CCU_DIRECT_BARRIER_MODE"; +constexpr const char* kRepositoryInstallWindowEnv = "TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW"; +constexpr const char* kRepositoryInstallDataLenModeEnv = "TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE"; +constexpr const char* kRepositoryMemoryAllocModeEnv = "TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE"; +constexpr const char* kInstallOrderEnv = "TILEXR_CCU_DIRECT_INSTALL_ORDER"; +constexpr const char* kResourceWindowTokenIdEnv = "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID"; +constexpr const char* kResourceWindowRawTokenIdEnv = "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID"; +constexpr const char* kResourceWindowTokenValueEnv = "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE"; +constexpr uint32_t kHcommStyleTask1PreludeInstructionCount = 5U; + +struct DeviceBuffer { + void* ptr = nullptr; + size_t bytes = 0; + + ~DeviceBuffer() + { + if (ptr != nullptr) { + aclrtFree(ptr); + } + } + + int Allocate(size_t size) + { + if (size == 0) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + bytes = size; + return aclrtMalloc(&ptr, bytes, ACL_MEM_MALLOC_HUGE_FIRST); + } +}; + +struct P2pCcuCopyState { + DeviceBuffer source; + DeviceBuffer destination; + std::vector expected; + std::vector observed; + size_t bytes = 0; + int initRet = ACL_SUCCESS; + int readRet = ACL_SUCCESS; + uint32_t mismatchCount = 0; + bool passed = false; +}; + +bool EnvFlag(const char* name) +{ + const char* value = std::getenv(name); + if (value == nullptr) { + return false; + } + const std::string text(value); + return text == "1" || text == "true" || text == "TRUE" || text == "yes" || text == "on"; +} + +bool ShouldFastExitAfterPrepareFailure(int ret) +{ + return ret != 0 && EnvFlag(kFastExitOnPrepareFailureEnv); +} + +const char* FastExitReasonForReturnCode(int ret) +{ + switch (ret) { + case 6: + return "prepare failed; skipping cleanup to preserve diagnostic status"; + case 8: + return "direct CCU stream synchronize failed; skipping cleanup to preserve diagnostic status"; + case 9: + return "direct CCU submit failed; skipping cleanup to preserve diagnostic status"; + case 13: + return "direct CCU collective completion timed out; skipping cleanup to preserve diagnostic status"; + case 14: + return "direct CCU P2P CCU-copy check failed; skipping cleanup to preserve diagnostic status"; + default: + return "direct CCU smoke failed; skipping cleanup to preserve diagnostic status"; + } +} + +bool ShouldFastExitAfterRun() +{ + return EnvFlag(kFastExitAfterRunEnv); +} + +void TraceLifecycle(const char* stage) +{ + if (!EnvFlag(kTraceLifecycleEnv)) { + return; + } + std::cout << "tilexr_ccu_direct_smoke lifecycle " << stage << std::endl; +} + +int ParseInt(const char* value, int fallback) +{ + if (value == nullptr || value[0] == '\0') { + return fallback; + } + char* end = nullptr; + const long parsed = std::strtol(value, &end, 0); + if (end == value || *end != '\0') { + return fallback; + } + return static_cast(parsed); +} + +int EnvInt(const char* name, int fallback) +{ + return ParseInt(std::getenv(name), fallback); +} + +uint64_t ParseU64(const char* value, uint64_t fallback) +{ + if (value == nullptr || value[0] == '\0') { + return fallback; + } + char* end = nullptr; + const unsigned long long parsed = std::strtoull(value, &end, 0); + if (end == value || *end != '\0') { + return fallback; + } + return static_cast(parsed); +} + +uint64_t EnvU64(const char* name, uint64_t fallback) +{ + return ParseU64(std::getenv(name), fallback); +} + +std::string RankEnvName(const char* prefix, int rank, const char* suffix) +{ + return std::string(prefix) + std::to_string(rank) + suffix; +} + +int RankEnvInt(const char* prefix, int rank, const char* suffix, const char* commonName, int fallback) +{ + const std::string rankName = RankEnvName(prefix, rank, suffix); + const char* rankValue = std::getenv(rankName.c_str()); + if (rankValue != nullptr && rankValue[0] != '\0') { + return ParseInt(rankValue, fallback); + } + return EnvInt(commonName, fallback); +} + +bool SyncCkeBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "sync_cke"; +} + +bool SyncCkeSetWaitBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "sync_cke_set_wait"; +} + +bool SyncCkePostOnlyBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "sync_cke_post_only"; +} + +bool LocalCkeBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "local_cke"; +} + +bool LocalCkePostOnlyBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "local_cke_post_only"; +} + +bool SyncXnPostOnlyBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "sync_xn_post_only"; +} + +bool SyncXnLoadPostOnlyBarrierMode() +{ + const char* value = std::getenv(kBarrierModeEnv); + return value != nullptr && std::string(value) == "sync_xn_load_post_only"; +} + +uint32_t RepositoryInstallWindowFromEnv() +{ + const char* value = std::getenv(kRepositoryInstallWindowEnv); + if (value == nullptr || value[0] == '\0') { + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_MISSION; + } + const std::string text(value); + if (text == "full_repository" || text == "full" || text == "1") { + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_FULL_REPOSITORY; + } + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_WINDOW_MISSION; +} + +uint32_t RepositoryInstallDataLenModeFromEnv() +{ + const char* value = std::getenv(kRepositoryInstallDataLenModeEnv); + if (value == nullptr || value[0] == '\0') { + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_INSTRUCTION_BYTES; + } + const std::string text(value); + if (text == "descriptor_bytes" || text == "descriptor" || text == "1") { + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_DESCRIPTOR_BYTES; + } + return TILEXR_DIRECT_CCU_REPOSITORY_INSTALL_DATA_LEN_INSTRUCTION_BYTES; +} + +uint32_t RepositoryMemoryAllocModeFromEnv() +{ + const char* value = std::getenv(kRepositoryMemoryAllocModeEnv); + if (value == nullptr || value[0] == '\0') { + return TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL; + } + const std::string text(value); + if (text == "acl_module3" || text == "acl_hccl_module" || text == "module3" || text == "1") { + return TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL_MODULE3; + } + if (text == "rt_hbm" || text == "rt" || text == "runtime_hbm" || text == "2") { + return TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_RT_HBM; + } + return TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_ACL; +} + +uint32_t InstallOrderFromEnv() +{ + const char* value = std::getenv(kInstallOrderEnv); + if (value == nullptr || value[0] == '\0') { + return TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST; + } + const std::string text(value); + if (text == "lower_layer_first" || text == "install_lower_layer_first" || text == "1") { + return TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST; + } + if (text == "repository_first" || text == "repo_first" || text == "0") { + return TILEXR_DIRECT_CCU_INSTALL_ORDER_REPOSITORY_FIRST; + } + return TILEXR_DIRECT_CCU_INSTALL_ORDER_REPOSITORY_FIRST; +} + +uint32_t DefaultSyncInstructionCount(uint32_t syncResourceCount) +{ + if (SyncCkeBarrierMode() || SyncCkeSetWaitBarrierMode()) { + return syncResourceCount * 2U + 1U; + } + if (SyncCkePostOnlyBarrierMode()) { + return syncResourceCount + 1U; + } + if (LocalCkePostOnlyBarrierMode()) { + return syncResourceCount; + } + if (SyncXnPostOnlyBarrierMode()) { + return kHcommStyleTask1PreludeInstructionCount + syncResourceCount; + } + if (SyncXnLoadPostOnlyBarrierMode()) { + return kHcommStyleTask1PreludeInstructionCount + syncResourceCount * 2U; + } + return kHcommStyleTask1PreludeInstructionCount + syncResourceCount * 2U; +} + +const char* FirstEnv(const char* a, const char* b, const char* c, const char* d) +{ + const char* value = std::getenv(a); + if (value != nullptr && value[0] != '\0') { + return value; + } + value = std::getenv(b); + if (value != nullptr && value[0] != '\0') { + return value; + } + value = std::getenv(c); + if (value != nullptr && value[0] != '\0') { + return value; + } + value = std::getenv(d); + return value != nullptr && value[0] != '\0' ? value : nullptr; +} + +int RankFromEnv() +{ + const char* rank = std::getenv("TILEXR_CCU_PROBE_RANK"); + if (rank != nullptr && rank[0] != '\0') { + return ParseInt(rank, 0); + } + return ParseInt(FirstEnv("PMI_RANK", "OMPI_COMM_WORLD_RANK", "MV2_COMM_WORLD_RANK", "RANK"), 0); +} + +int RankSizeFromEnv() +{ + const char* rankSize = std::getenv("TILEXR_CCU_PROBE_RANK_SIZE"); + if (rankSize != nullptr && rankSize[0] != '\0') { + return ParseInt(rankSize, 1); + } + return ParseInt(FirstEnv("PMI_SIZE", "OMPI_COMM_WORLD_SIZE", "MV2_COMM_WORLD_SIZE", "RANK_SIZE"), 1); +} + +std::vector BuildP2pCcuCopyPattern(int rank, size_t bytes) +{ + std::vector pattern(bytes); + for (size_t i = 0; i < bytes; ++i) { + pattern[i] = static_cast((static_cast(rank + 1) * 17U + i * 13U) & 0xffU); + } + return pattern; +} + +int InitP2pCcuCopyState(int rank, int peer, P2pCcuCopyState* state) +{ + if (state == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + state->bytes = static_cast(EnvInt(kP2pCcuCopyBytesEnv, 4096)); + if (state->bytes == 0) { + state->initRet = TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + return state->initRet; + } + state->expected = BuildP2pCcuCopyPattern(peer, state->bytes); + state->observed.assign(state->bytes, 0); + const std::vector source = BuildP2pCcuCopyPattern(rank, state->bytes); + std::vector destination(state->bytes, 0xa5U); + + int ret = state->source.Allocate(state->bytes); + if (ret != ACL_SUCCESS) { + state->initRet = ret; + return ret; + } + ret = state->destination.Allocate(state->bytes); + if (ret != ACL_SUCCESS) { + state->initRet = ret; + return ret; + } + ret = aclrtMemcpy( + state->source.ptr, + state->bytes, + source.data(), + state->bytes, + ACL_MEMCPY_HOST_TO_DEVICE); + if (ret != ACL_SUCCESS) { + state->initRet = ret; + return ret; + } + ret = aclrtMemcpy( + state->destination.ptr, + state->bytes, + destination.data(), + state->bytes, + ACL_MEMCPY_HOST_TO_DEVICE); + state->initRet = ret; + return ret; +} + +int CheckP2pCcuCopyState(P2pCcuCopyState* state) +{ + if (state == nullptr || state->destination.ptr == nullptr || state->bytes == 0) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + state->readRet = aclrtMemcpy( + state->observed.data(), + state->observed.size(), + state->destination.ptr, + state->bytes, + ACL_MEMCPY_DEVICE_TO_HOST); + if (state->readRet != ACL_SUCCESS) { + return state->readRet; + } + state->mismatchCount = 0; + for (size_t i = 0; i < state->bytes; ++i) { + if (state->observed[i] != state->expected[i]) { + ++state->mismatchCount; + } + } + state->passed = state->mismatchCount == 0; + return state->passed ? ACL_SUCCESS : TileXR::TILEXR_ERROR_INTERNAL; +} + +void PrintP2pCcuCopy( + int rank, + int peer, + const P2pCcuCopyState& state, + int prepareRet, + int submitRet, + int syncRet) +{ + const uint32_t firstObserved = state.observed.empty() ? 0U : state.observed.front(); + const uint32_t firstExpected = state.expected.empty() ? 0U : state.expected.front(); + std::cout << "tilexr_ccu_direct_smoke p2pCcuCopy" + << " rank=" << rank + << " peer=" << peer + << " bytes=" << state.bytes + << " initRet=" << state.initRet + << " prepareRet=" << prepareRet + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " readRet=" << state.readRet + << " mismatches=" << state.mismatchCount + << " firstObserved=0x" << std::hex << firstObserved + << " firstExpected=0x" << firstExpected + << std::dec + << " passed=" << (state.passed ? 1 : 0) + << std::endl; +} + +int RunP2pCcuCopy( + int rank, + int peer, + P2pCcuCopyState* state, + int prepareRet, + int submitRet, + int syncRet) +{ + if (state == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (syncRet == ACL_SUCCESS && submitRet == TileXR::TILEXR_SUCCESS) { + (void)CheckP2pCcuCopyState(state); + } + PrintP2pCcuCopy(rank, peer, *state, prepareRet, submitRet, syncRet); + return state->passed ? TileXR::TILEXR_SUCCESS : TileXR::TILEXR_ERROR_INTERNAL; +} + +int DeviceFromList(const char* devices, int rank, int fallback) +{ + if (devices == nullptr || devices[0] == '\0') { + return fallback; + } + std::string list(devices); + size_t start = 0; + int index = 0; + while (start <= list.size()) { + const size_t comma = list.find(',', start); + const size_t end = comma == std::string::npos ? list.size() : comma; + if (index == rank && end > start) { + return ParseInt(list.substr(start, end - start).c_str(), fallback); + } + if (comma == std::string::npos) { + break; + } + start = comma + 1; + ++index; + } + return fallback; +} + +int DeviceFromEnv(int rank) +{ + const char* explicitDevice = std::getenv("TILEXR_CCU_PROBE_DEVICE"); + if (explicitDevice != nullptr && explicitDevice[0] != '\0') { + return ParseInt(explicitDevice, 0); + } + const int firstDevice = EnvInt("TILEXR_TEST_FIRST_NPU", 0); + return DeviceFromList(std::getenv("TILEXR_TEST_DEVICES"), rank, firstDevice + rank); +} + +int InitCommForDirectCcuSmoke(int commDomain, int rankSize, int rank, TileXRCommPtr* raw) +{ + if (EnvFlag(kDirectCcuOnlyInitEnv)) { + return TileXRCommInitRankDirectCcuWithDomain(commDomain, rankSize, rank, raw); + } + return TileXRCommInitRankWithDomain(commDomain, rankSize, rank, raw); +} + +TileXRDirectCcuPrepareOptions MakePrepareOptions(int rank, int rankSize, int device) +{ + TileXRDirectCcuPrepareOptions options {}; + options.syncResourceCount = static_cast(EnvInt("TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT", 1)); + options.sqeArgCount = + static_cast(EnvInt("TILEXR_CCU_PROBE_SQE_ARG_COUNT", TILEXR_DIRECT_CCU_SQE_ARGS_LEN)); + const uint32_t defaultSyncInstructionCount = DefaultSyncInstructionCount(options.syncResourceCount); + options.syncInstructionCount = + static_cast(EnvInt("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT", defaultSyncInstructionCount)); + options.bindingsPerSyncResource = static_cast(EnvInt("TILEXR_CCU_PROBE_BINDINGS_PER_RESOURCE", 1)); + options.missionStartId = static_cast(EnvInt("TILEXR_CCU_PROBE_MISSION_START", 1)); + options.instructionStartId = static_cast(EnvInt("TILEXR_CCU_PROBE_INSTRUCTION_START", 1)); + options.missionInstructionStartId = + static_cast(EnvInt("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START", 0)); + options.xnStartId = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_XN_START", "TILEXR_CCU_PROBE_XN_START", 1)); + options.gsaStartId = static_cast(EnvInt("TILEXR_CCU_PROBE_GSA_START", 0)); + options.remoteXnStartId = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_REMOTE_XN_START", "TILEXR_CCU_PROBE_REMOTE_XN_START", 0)); + options.remoteXnCount = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_REMOTE_XN_COUNT", "TILEXR_CCU_PROBE_REMOTE_XN_COUNT", 0)); + options.ckeStartId = static_cast(EnvInt("TILEXR_CCU_PROBE_CKE_START", 1)); + options.channelStartId = static_cast(EnvInt("TILEXR_CCU_PROBE_CHANNEL_START", 1)); + options.localWaitCkeStartId = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_LOCAL_WAIT_CKE_START", kLocalWaitCkeStartEnv, 0)); + options.localWaitCkeCount = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_LOCAL_WAIT_CKE_COUNT", kLocalWaitCkeCountEnv, 0)); + options.remoteNotifyCkeStartId = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_REMOTE_NOTIFY_CKE_START", kRemoteNotifyCkeStartEnv, 0)); + options.remoteNotifyCkeCount = static_cast( + RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_REMOTE_NOTIFY_CKE_COUNT", kRemoteNotifyCkeCountEnv, 0)); + options.repositoryInstallWindow = RepositoryInstallWindowFromEnv(); + options.repositoryInstallDataLenMode = RepositoryInstallDataLenModeFromEnv(); + options.repositoryMemoryAllocMode = RepositoryMemoryAllocModeFromEnv(); + options.installOrder = InstallOrderFromEnv(); + options.deviceId = static_cast(device); + options.rank = static_cast(rank); + options.provider = rankSize > 0 ? "tilexr-direct-smoke-probe" : ""; + return options; +} + +void PrintInstallReport( + const char* prefix, + int ret, + const TileXRDirectCcuPrepareReport& report) +{ + std::cout << prefix + << " ret=" << ret + << " pipelineBuilt=" << (report.pipelineBuilt ? 1 : 0) + << " installAttempted=" << (report.installAttempted ? 1 : 0) + << " installSucceeded=" << (report.installSucceeded ? 1 : 0) + << " submitReady=" << (report.submitReady ? 1 : 0) + << " requiredInstallSurfaceCount=" << report.requiredInstallSurfaceCount + << " publicVerifiedInstallSurfaceCount=" << report.publicVerifiedInstallSurfaceCount + << " missingInstallSurfaceCount=" << report.missingInstallSurfaceCount + << " taskCount=" << report.taskCount + << " submitTaskCount=" << report.submitTaskCount + << " message=\"" << report.message << "\"" + << std::endl; +} + +void PrintSubmitReport( + const char* prefix, + int ret, + const TileXRDirectCcuSubmitReport& report) +{ + std::cout << prefix + << " ret=" << ret + << " submitted=" << (report.submitted ? 1 : 0) + << " taskCount=" << report.taskCount + << " submittedTaskCount=" << report.submittedTaskCount + << " message=\"" << report.message << "\"" + << std::endl; +} + +int SubmitPreparedWithSelector( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t taskCount, + void* stream, + TileXRDirectCcuSubmitReport* report) +{ + const char* value = std::getenv(kSubmitTaskSelectorEnv); + const std::string selector = value == nullptr ? "all" : std::string(value); + if (selector.empty() || selector == "all") { + return TileXRDirectCcuSubmitPrepared(prepared, stream, report); + } + const bool selectFirst = selector == "first"; + const bool selectSecond = selector == "second"; + if (!selectFirst && !selectSecond) { + if (report != nullptr) { + *report = TileXRDirectCcuSubmitReport {}; + std::strncpy( + report->message, + "invalid TILEXR_CCU_DIRECT_SMOKE_SUBMIT_TASK_SELECTOR", + sizeof(report->message) - 1U); + } + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const uint32_t selectedIndex = selectFirst ? 0U : 1U; + std::cout << "tilexr_ccu_direct_smoke submitTaskSelector=" + << selector + << " selectedIndex=" << selectedIndex + << " preparedTaskCount=" << taskCount + << std::endl; + if (selectedIndex >= taskCount) { + if (report != nullptr) { + *report = TileXRDirectCcuSubmitReport {}; + report->taskCount = taskCount; + std::strncpy(report->message, "selected direct CCU submit task is missing", sizeof(report->message) - 1U); + } + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + return TileXRDirectCcuSubmitPreparedTask(prepared, selectedIndex, stream, report); +} + +void PrintSubmitTiming( + int rank, + int delayMs, + int submitRet, + int syncRet, + long long submitMs, + long long syncMs) +{ + std::cout << "tilexr_ccu_direct_smoke submitTiming" + << " rank=" << rank + << " preSubmitDelayMs=" << delayMs + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " submitMs=" << submitMs + << " syncMs=" << syncMs + << std::endl; +} + +void PrintConfig( + const TileXRDirectCcuPrepareOptions& options, + int rankSize) +{ + std::cout << "tilexr_ccu_direct_smoke config" + << " rank=" << options.rank + << " rankSize=" << rankSize + << " device=" << options.deviceId + << " syncResourceCount=" << options.syncResourceCount + << " sqeArgCount=" << options.sqeArgCount + << " syncInstructionCount=" << options.syncInstructionCount + << " bindingsPerSyncResource=" << options.bindingsPerSyncResource + << " missionStartId=" << options.missionStartId + << " instructionStartId=" << options.instructionStartId + << " missionInstructionStartId=" << options.missionInstructionStartId + << " xnStartId=" << options.xnStartId + << " gsaStartId=" << options.gsaStartId + << " remoteXnStartId=" << options.remoteXnStartId + << " remoteXnCount=" << options.remoteXnCount + << " ckeStartId=" << options.ckeStartId + << " channelStartId=" << options.channelStartId + << " localWaitCkeStartId=" << options.localWaitCkeStartId + << " localWaitCkeCount=" << options.localWaitCkeCount + << " remoteNotifyCkeStartId=" << options.remoteNotifyCkeStartId + << " remoteNotifyCkeCount=" << options.remoteNotifyCkeCount + << " repositoryInstallWindow=" << options.repositoryInstallWindow + << " repositoryInstallDataLenMode=" << options.repositoryInstallDataLenMode + << " repositoryMemoryAllocMode=" << options.repositoryMemoryAllocMode + << " installOrder=" << options.installOrder + << " barrierMode=\"" << (std::getenv(kBarrierModeEnv) == nullptr ? "" : std::getenv(kBarrierModeEnv)) + << "\"" + << " resourceWindowTokenId=\"" + << (std::getenv(kResourceWindowTokenIdEnv) == nullptr ? "" : std::getenv(kResourceWindowTokenIdEnv)) + << "\"" + << " resourceWindowRawTokenId=\"" + << (std::getenv(kResourceWindowRawTokenIdEnv) == nullptr ? "" : std::getenv(kResourceWindowRawTokenIdEnv)) + << "\"" + << " resourceWindowTokenValue=\"" + << (std::getenv(kResourceWindowTokenValueEnv) == nullptr ? "" : std::getenv(kResourceWindowTokenValueEnv)) + << "\"" + << " provider=\"" << (options.provider == nullptr ? "" : options.provider) << "\"" + << std::endl; +} + +void PrintPreparedTasks(TileXRDirectCcuPreparedTasksPtr prepared, uint32_t taskCount) +{ + std::cout << "tilexr_ccu_direct_smoke preparedTasks" + << " count=" << taskCount; + const uint32_t previewCount = taskCount < 2U ? taskCount : 2U; + for (size_t i = 0; i < previewCount; ++i) { + TileXRDirectCcuTaskInfo task; + const int ret = TileXRDirectCcuGetPreparedTask(prepared, static_cast(i), &task); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cout << " task" << i << ".ret=" << ret; + continue; + } + std::cout << " task" << i + << ".dieId=" << static_cast(task.dieId) + << " task" << i << ".missionId=" << static_cast(task.missionId) + << " task" << i << ".timeout=" << task.timeout + << " task" << i << ".instStartId=" << task.instStartId + << " task" << i << ".instCnt=" << task.instCnt + << " task" << i << ".key=0x" << std::hex << task.key << std::dec + << " task" << i << ".argSize=" << task.argSize; + for (uint32_t arg = 0; arg < TILEXR_DIRECT_CCU_SQE_ARGS_LEN; ++arg) { + std::cout << " task" << i + << ".arg" << arg << "=0x" + << std::hex << task.args[arg] << std::dec; + } + } + std::cout << std::endl; +} + +void PrintInstructionReadback(TileXRCommPtr raw, TileXRDirectCcuPreparedTasksPtr prepared, uint32_t taskCount) +{ + if (!EnvFlag(kReadbackInstructionsEnv)) { + return; + } + if (raw == nullptr) { + std::cout << "tilexr_ccu_direct_smoke instructionReadback ret=" + << TileXR::TILEXR_ERROR_PARA_CHECK_FAIL + << " message=\"missing TileXRComm\"" + << std::endl; + return; + } + for (uint32_t taskIndex = 0; taskIndex < taskCount; ++taskIndex) { + TileXRDirectCcuTaskInfo task; + const int taskRet = TileXRDirectCcuGetPreparedTask(prepared, taskIndex, &task); + if (taskRet != TileXR::TILEXR_SUCCESS) { + std::cout << "tilexr_ccu_direct_smoke instructionReadback" + << " task=" << taskIndex + << " ret=" << taskRet + << " message=\"failed to read prepared task\"" + << std::endl; + continue; + } + + constexpr uint32_t kMaxReadbackInstructionCount = 8U; + const uint32_t readCount = task.instCnt < kMaxReadbackInstructionCount ? + task.instCnt : kMaxReadbackInstructionCount; + std::vector readback(readCount); + TileXRDirectCcuInstructionReadbackReport report; + const int readRet = TileXRCommReadDirectCcuInstructions( + raw, + static_cast(task.dieId), + static_cast(task.instStartId), + readCount, + readback.data(), + &report); + std::cout << "tilexr_ccu_direct_smoke instructionReadback" + << " task=" << taskIndex + << " ret=" << readRet + << " dieId=" << static_cast(task.dieId) + << " instStartId=" << task.instStartId + << " requestedCount=" << task.instCnt + << " readCount=" << readCount + << " opcode=" << report.opcode + << " driverRet=" << report.driverRet + << " opRet=" << report.opRet + << " message=\"" << report.message << "\""; + if (readRet == TileXR::TILEXR_SUCCESS) { + for (uint32_t i = 0; i < readCount; ++i) { + std::cout << " instr" << i << "="; + for (uint32_t word = 0; word < 4U; ++word) { + if (word != 0) { + std::cout << ","; + } + std::cout << "0x" << std::hex << std::nouppercase << readback[i].words[word] << std::dec; + } + } + } + std::cout << std::endl; + } +} + +bool CollectiveSubmitReadyGateConfigured() +{ + const char* readyDir = std::getenv(kReadyDirEnv); + return readyDir != nullptr && readyDir[0] != '\0'; +} + +std::string SubmitReadinessPath(int rank) +{ + const char* readyDir = std::getenv(kReadyDirEnv); + if (readyDir == nullptr || readyDir[0] == '\0') { + return {}; + } + return std::string(readyDir) + "/rank" + std::to_string(rank) + ".ready"; +} + +bool WriteSubmitReadiness(int rank, bool ready) +{ + const std::string path = SubmitReadinessPath(rank); + if (path.empty()) { + return true; + } + std::ofstream out(path.c_str(), std::ios::out | std::ios::trunc); + if (!out) { + return false; + } + out << (ready ? 1 : 0) << "\n"; + return static_cast(out); +} + +bool ReadSubmitReadiness(int rank, bool* ready) +{ + if (ready == nullptr) { + return false; + } + const std::string path = SubmitReadinessPath(rank); + if (path.empty()) { + return false; + } + std::ifstream in(path.c_str()); + if (!in) { + return false; + } + int value = 0; + in >> value; + if (!in) { + return false; + } + *ready = value != 0; + return true; +} + +bool WaitForCollectiveSubmitReadiness(int rank, int rankSize, bool localReady) +{ + if (!CollectiveSubmitReadyGateConfigured()) { + return localReady; + } + const bool wrote = WriteSubmitReadiness(rank, localReady); + const int timeoutMs = EnvInt(kReadyTimeoutMsEnv, 5000); + const auto start = std::chrono::steady_clock::now(); + for (;;) { + bool allSeen = wrote; + bool allReady = wrote && localReady; + for (int peer = 0; peer < rankSize; ++peer) { + bool peerReady = false; + if (!ReadSubmitReadiness(peer, &peerReady)) { + allSeen = false; + allReady = false; + break; + } + allReady = allReady && peerReady; + } + if (allSeen) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitReady" + << " rank=" << rank + << " localReady=" << (localReady ? 1 : 0) + << " allRanksReady=" << (allReady ? 1 : 0) + << std::endl; + return allReady; + } + const auto elapsedMs = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start).count(); + if (elapsedMs >= timeoutMs) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitReady" + << " rank=" << rank + << " localReady=" << (localReady ? 1 : 0) + << " allRanksReady=0" + << " timeoutMs=" << timeoutMs + << std::endl; + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +bool CollectiveSubmitDoneGateConfigured() +{ + const char* doneDir = std::getenv(kDoneDirEnv); + return doneDir != nullptr && doneDir[0] != '\0'; +} + +std::string SubmitDonePath(int rank) +{ + const char* doneDir = std::getenv(kDoneDirEnv); + if (doneDir == nullptr || doneDir[0] == '\0') { + return {}; + } + return std::string(doneDir) + "/rank" + std::to_string(rank) + ".done"; +} + +bool WriteSubmitDone(int rank, int result) +{ + const std::string path = SubmitDonePath(rank); + if (path.empty()) { + return true; + } + std::ofstream out(path.c_str(), std::ios::out | std::ios::trunc); + if (!out) { + return false; + } + out << result << "\n"; + return static_cast(out); +} + +bool ReadSubmitDone(int rank, int* result) +{ + if (result == nullptr) { + return false; + } + const std::string path = SubmitDonePath(rank); + if (path.empty()) { + return false; + } + std::ifstream in(path.c_str()); + if (!in) { + return false; + } + in >> *result; + return static_cast(in); +} + +bool WaitForCollectiveSubmitDone(int rank, int rankSize, int localResult) +{ + if (!CollectiveSubmitDoneGateConfigured()) { + return true; + } + const bool wrote = WriteSubmitDone(rank, localResult); + const int timeoutMs = EnvInt(kReadyTimeoutMsEnv, 5000); + const auto start = std::chrono::steady_clock::now(); + for (;;) { + bool allSeen = wrote; + bool allSucceeded = wrote && localResult == 0; + for (int peer = 0; peer < rankSize; ++peer) { + int peerResult = 0; + if (!ReadSubmitDone(peer, &peerResult)) { + allSeen = false; + allSucceeded = false; + break; + } + allSucceeded = allSucceeded && peerResult == 0; + } + if (allSeen) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitDone" + << " rank=" << rank + << " localResult=" << localResult + << " allRanksDone=1" + << " allRanksSucceeded=" << (allSucceeded ? 1 : 0) + << std::endl; + return true; + } + const auto elapsedMs = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start).count(); + if (elapsedMs >= timeoutMs) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitDone" + << " rank=" << rank + << " localResult=" << localResult + << " allRanksDone=0" + << " timeoutMs=" << timeoutMs + << std::endl; + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +int RunPreparedSmokeForRank(TileXRCommPtr raw, int rank, int rankSize, int device) +{ + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + const int peer = rankSize == 2 ? 1 - rank : (rank + 1) % rankSize; + const bool p2pCcuCopyEnabled = EnvFlag(kP2pCcuCopyEnv); + P2pCcuCopyState p2pCcuCopy; + if (p2pCcuCopyEnabled) { + options.syncResourceCount = 1; + options.sqeArgCount = 0; + options.syncInstructionCount = 7; + p2pCcuCopy.initRet = InitP2pCcuCopyState(rank, peer, &p2pCcuCopy); + } + PrintConfig(options, rankSize); + + TileXRDirectCcuPreparedTasksPtr prepared = nullptr; + TileXRDirectCcuPrepareReport installReport; + int ret = p2pCcuCopyEnabled && p2pCcuCopy.initRet != ACL_SUCCESS ? + p2pCcuCopy.initRet : + p2pCcuCopyEnabled ? + [&]() { + TileXRDirectCcuMemoryCopyPrepareOptions copyOptions {}; + copyOptions.prepare = options; + copyOptions.localSourceAddr = reinterpret_cast(p2pCcuCopy.source.ptr); + copyOptions.localDestinationAddr = reinterpret_cast(p2pCcuCopy.destination.ptr); + copyOptions.bytes = p2pCcuCopy.bytes; + copyOptions.peerRank = static_cast(peer); + copyOptions.direction = TILEXR_DIRECT_CCU_MEMORY_COPY_REMOTE_TO_LOCAL; + return TileXRCommPrepareDirectCcuMemoryCopy(raw, ©Options, &prepared, &installReport); + }() : + TileXRCommPrepareDirectCcu(raw, &options, &prepared, &installReport); + PrintInstallReport("tilexr_ccu_direct_smoke prepare", ret, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(raw, prepared, installReport.submitTaskCount); + + int finalRet = 0; + const bool submitRequested = EnvFlag(kSubmitEnv); + const bool collectiveSubmitReady = submitRequested ? + WaitForCollectiveSubmitReadiness( + rank, + rankSize, + ret == TileXR::TILEXR_SUCCESS && installReport.submitReady) : + false; + if (ret != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (submitRequested && !collectiveSubmitReady && CollectiveSubmitReadyGateConfigured()) { + std::cout << "tilexr_ccu_direct_smoke submit skipped reason=\"collective submitReady gate did not pass\"" + << " localSubmitReady=" << (installReport.submitReady ? 1 : 0) + << std::endl; + } else if (submitRequested && !installReport.submitReady) { + std::cout << "tilexr_ccu_direct_smoke submit skipped reason=\"prepare did not reach submitReady\"" + << std::endl; + } else if (submitRequested) { + aclrtStream stream = nullptr; + int streamRet = aclrtCreateStream(&stream); + if (streamRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke aclrtCreateStream ret=" << streamRet << std::endl; + finalRet = 7; + } else { + const int delayRank = EnvInt(kDelayRankEnv, -1); + const int preSubmitDelayMs = EnvInt(kPreSubmitDelayMsEnv, 0); + const int effectiveDelayMs = rank == delayRank && preSubmitDelayMs > 0 ? preSubmitDelayMs : 0; + if (effectiveDelayMs > 0) { + std::cout << "tilexr_ccu_direct_smoke preSubmitDelay" + << " rank=" << rank + << " delayMs=" << effectiveDelayMs + << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(effectiveDelayMs)); + } + + TileXRDirectCcuSubmitReport submitReport; + const auto submitBegin = std::chrono::steady_clock::now(); + const char* submitTaskSelector = std::getenv(kSubmitTaskSelectorEnv); + const bool submitAllTasks = submitTaskSelector == nullptr || submitTaskSelector[0] == '\0' || + std::string(submitTaskSelector) == "all"; + const int submitRet = submitAllTasks ? + TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport) : + SubmitPreparedWithSelector(prepared, installReport.submitTaskCount, stream, &submitReport); + const auto submitEnd = std::chrono::steady_clock::now(); + PrintSubmitReport("tilexr_ccu_direct_smoke submit", submitRet, submitReport); + const auto syncBegin = std::chrono::steady_clock::now(); + TraceLifecycle("before aclrtSynchronizeStream"); + const int syncRet = aclrtSynchronizeStream(stream); + TraceLifecycle("after aclrtSynchronizeStream"); + const auto syncEnd = std::chrono::steady_clock::now(); + PrintSubmitTiming( + rank, + effectiveDelayMs, + submitRet, + syncRet, + std::chrono::duration_cast(submitEnd - submitBegin).count(), + std::chrono::duration_cast(syncEnd - syncBegin).count()); + if (syncRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke aclrtSynchronizeStream ret=" << syncRet << std::endl; + finalRet = 8; + } else if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } + if (p2pCcuCopyEnabled) { + const int p2pCcuCopyRet = RunP2pCcuCopy(rank, peer, &p2pCcuCopy, ret, submitRet, syncRet); + if (p2pCcuCopyRet != TileXR::TILEXR_SUCCESS && finalRet == 0) { + finalRet = 14; + } + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet) && finalRet == 0) { + finalRet = 13; + } + TraceLifecycle("before aclrtDestroyStream"); + aclrtDestroyStream(stream); + TraceLifecycle("after aclrtDestroyStream"); + } + } + + if (prepared != nullptr && ShouldFastExitAfterRun()) { + std::cout << "tilexr_ccu_direct_smoke fastExitAfterRun=1" + << " ret=" << finalRet + << " reason=\"skipping prepared-task cleanup to isolate cleanup hangs\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } + if (prepared != nullptr) { + TraceLifecycle("before TileXRDirectCcuDestroyPrepared"); + const int preparedDestroyRet = TileXRDirectCcuDestroyPrepared(prepared); + TraceLifecycle("after TileXRDirectCcuDestroyPrepared"); + if (preparedDestroyRet != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke prepared destroy ret=" << preparedDestroyRet << std::endl; + finalRet = finalRet == 0 ? 11 : finalRet; + } + } + return finalRet; +} + +int RunThreadModeSmoke(int rankSize) +{ + std::cout << "tilexr_ccu_direct_smoke threadMode begin" + << " rankSize=" << rankSize + << std::endl; + std::vector devices(static_cast(rankSize)); + for (int rank = 0; rank < rankSize; ++rank) { + devices[rank] = DeviceFromEnv(rank); + } + std::vector comms(static_cast(rankSize), nullptr); + int ret = TileXRCommInitAll(static_cast(rankSize), devices.data(), comms.data()); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke threadMode comm init ret=" << ret << std::endl; + return 5; + } + + std::vector rankResults(static_cast(rankSize), 0); + std::vector rankThreads; + rankThreads.reserve(static_cast(rankSize)); + for (int rank = 0; rank < rankSize; ++rank) { + rankThreads.emplace_back([&, rank]() { + const int setDeviceRet = aclrtSetDevice(devices[rank]); + if (setDeviceRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke threadMode aclrtSetDevice ret=" + << setDeviceRet + << " rank=" << rank + << " device=" << devices[rank] + << std::endl; + rankResults[rank] = 14; + return; + } + rankResults[rank] = RunPreparedSmokeForRank(comms[rank], rank, rankSize, devices[rank]); + }); + } + for (auto& rankThread : rankThreads) { + rankThread.join(); + } + + int finalRet = 0; + for (int rank = 0; rank < rankSize; ++rank) { + std::cout << "tilexr_ccu_direct_smoke threadMode rank=" << rank + << " ret=" << rankResults[rank] + << std::endl; + if (rankResults[rank] != 0 && finalRet == 0) { + finalRet = rankResults[rank]; + } + } + for (auto comm : comms) { + if (comm == nullptr) { + continue; + } + const int destroyRet = TileXRCommDestroy(comm); + if (destroyRet != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke threadMode destroy ret=" << destroyRet << std::endl; + finalRet = finalRet == 0 ? 10 : finalRet; + } + } + return finalRet; +} + +} // namespace + +int main() +{ + if (!EnvFlag(kEnableEnv)) { + std::cout << "tilexr_ccu_direct_smoke skipped set " + << kEnableEnv << "=1 to run private C++ integration probe" + << std::endl; + return 0; + } + + const int rank = RankFromEnv(); + const int rankSize = RankSizeFromEnv(); + const int device = DeviceFromEnv(rank); + const int commDomain = EnvInt("TILEXR_CCU_PROBE_COMM_DOMAIN", 0); + + if (rankSize <= 1) { + std::cout << "tilexr_ccu_direct_smoke skipped rankSize=" << rankSize + << " reason=\"direct CCU prepare requires a multi-rank communicator\"" + << std::endl; + return 0; + } + if (rank < 0 || rank >= rankSize) { + std::cerr << "tilexr_ccu_direct_smoke invalid rank=" << rank + << " rankSize=" << rankSize << std::endl; + return 2; + } + if (EnvFlag(kThreadModeEnv)) { + const int aclRet = aclInit(nullptr); + if (aclRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke aclInit ret=" << aclRet << std::endl; + return 3; + } + const int threadRet = RunThreadModeSmoke(rankSize); + aclFinalize(); + return threadRet; + } + + std::cout << "tilexr_ccu_direct_smoke begin" + << " rank=" << rank + << " rankSize=" << rankSize + << " device=" << device + << " commDomain=" << commDomain + << std::endl; + + int ret = aclInit(nullptr); + if (ret != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke aclInit ret=" << ret << std::endl; + return 3; + } + + ret = aclrtSetDevice(device); + if (ret != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke aclrtSetDevice ret=" << ret + << " device=" << device << std::endl; + aclFinalize(); + return 4; + } + + TileXRCommPtr raw = nullptr; + ret = InitCommForDirectCcuSmoke(commDomain, rankSize, rank, &raw); + if (ret != TileXR::TILEXR_SUCCESS || raw == nullptr) { + std::cerr << "tilexr_ccu_direct_smoke comm init ret=" << ret + << " raw=" << raw << std::endl; + aclrtResetDevice(device); + aclFinalize(); + return 5; + } + + int finalRet = RunPreparedSmokeForRank(raw, rank, rankSize, device); + if (ShouldFastExitAfterPrepareFailure(finalRet)) { + std::cout << "tilexr_ccu_direct_smoke fastExitOnPrepareFailure=1" + << " ret=" << finalRet + << " reason=\"" << FastExitReasonForReturnCode(finalRet) << "\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } + if (ShouldFastExitAfterRun()) { + std::cout << "tilexr_ccu_direct_smoke fastExitAfterRun=1" + << " ret=" << finalRet + << " reason=\"skipping communicator cleanup to isolate cleanup hangs\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } + TraceLifecycle("before TileXRCommDestroy"); + const int destroyRet = TileXRCommDestroy(raw); + TraceLifecycle("after TileXRCommDestroy"); + if (destroyRet != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke destroy ret=" << destroyRet << std::endl; + finalRet = finalRet == 0 ? 10 : finalRet; + } + TraceLifecycle("before aclrtResetDevice"); + aclrtResetDevice(device); + TraceLifecycle("after aclrtResetDevice"); + TraceLifecycle("before aclFinalize"); + aclFinalize(); + TraceLifecycle("after aclFinalize"); + return finalRet; +} diff --git a/tests/ccu/check_tile_comm_no_hcomm_deps.sh b/tests/ccu/check_tile_comm_no_hcomm_deps.sh new file mode 100644 index 0000000..a0e8ba9 --- /dev/null +++ b/tests/ccu/check_tile_comm_no_hcomm_deps.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +lib="${1:-install/lib/libtile-comm.so}" + +if [ ! -f "${lib}" ]; then + echo "ERROR: ${lib} not found" >&2 + exit 1 +fi + +if command -v readelf >/dev/null 2>&1; then + needed=$(readelf -d "${lib}" 2>/dev/null | grep -E 'NEEDED' || true) +else + needed="" +fi + +deps=$(ldd "${lib}" 2>/dev/null || true) +forbidden='libhcomm\.so|libhccl_v2\.so|libhccl_fwk\.so|libmc2_client\.so|HcclCcuKernel|HcclGetCcuTaskInfo|HcomGetCcuTaskInfo|HcclChannelAcquire|HcclGetChannelForCcu|HcclAllocAlgResourceCcu|HcommChannelNotify|HcommChannelFence|rtGetNotifyAddress|HrtCcuLaunch|HrtGetDevResAddress|HrtReleaseDevResAddress|HrtNotifyGetAddr|HrtRaCustomChannel|HrtCntNotify|CcuResBatchAllocator|CcuResRepository|CcuDeviceManager|CcuDevMgrImp|CcuRepContext|CcuKernelMgr|CtxMgrImp|CcuInstrInfo|CcuTaskParam|CcuTaskArg|GeneTaskParam|GetMissionKey|SetMissionId|SetMissionKey|SetInstrId|SetCcuInstrInfo|LoadInstruction|AllocIns|AllocCke|AllocXn|COMM_ENGINE_CCU|COMM_PROTOCOL_UBC_CTP|HCCL_SERVER_TYPE_CCU|RT_RES_TYPE_CCU_CKE|RT_RES_TYPE_CCU_XN' + +printf '%s\n' "${needed}" +printf '%s\n' "${deps}" + +if printf '%s\n%s\n' "${needed}" "${deps}" | grep -E "${forbidden}" >/dev/null; then + echo "ERROR: libtile-comm.so links an hcomm/HCCL CCU reference library" >&2 + exit 1 +fi + +if command -v nm >/dev/null 2>&1; then + symbol_hits=$(nm -D "${lib}" 2>/dev/null | c++filt | grep -E "${forbidden}" || true) +else + symbol_hits="" +fi +if [ -n "${symbol_hits}" ]; then + printf '%s\n' "${symbol_hits}" >&2 + echo "ERROR: libtile-comm.so exports or imports private hcomm/HCCL CCU symbols" >&2 + exit 1 +fi + +if command -v strings >/dev/null 2>&1; then + string_hits=$(strings -a "${lib}" 2>/dev/null | grep -E "${forbidden}" || true) +else + string_hits="" +fi +if [ -n "${string_hits}" ]; then + printf '%s\n' "${string_hits}" >&2 + echo "ERROR: libtile-comm.so contains private hcomm/HCCL CCU references" >&2 + exit 1 +fi + +echo "TileXR CCU dependency guard passed: no hcomm/HCCL private CCU dependency or symbol reference" diff --git a/tests/ccu/run_tilexr_ccu_direct_evening_smoke.sh b/tests/ccu/run_tilexr_ccu_direct_evening_smoke.sh new file mode 100644 index 0000000..142552d --- /dev/null +++ b/tests/ccu/run_tilexr_ccu_direct_evening_smoke.sh @@ -0,0 +1,538 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2026 TileXR Project +# +# One-shot direct CCU bring-up wrapper for the reserved 20:00+ hardware window. +# It still fails closed: submit/barrier/P2P stages run only after prepare logs +# show submitReady=1 for both ranks. + +set -euo pipefail + +for arg in "$@"; do + case "${arg}" in + --dry-run) + export TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN=1 + ;; + *) + echo "ERROR: unknown argument: ${arg}" >&2 + exit 2 + ;; + esac +done + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "${repo_root}" + +set_evening_defaults() +{ + export TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 + export TILEXR_CCU_SMOKE_REQUIRE_NPU_SMI=1 + if [ "${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE:-}" = "" ]; then + export TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=0 + else + export TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE + fi + export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}" + export TILEXR_CCU_SMOKE_DEVICES="${TILEXR_CCU_SMOKE_DEVICES:-0,1}" + export TILEXR_CCU_DIRECT_TRACE="${TILEXR_CCU_DIRECT_TRACE:-1}" + export TILEXR_LOG_LEVEL="${TILEXR_LOG_LEVEL:-INFO}" + export TILEXR_CCU_SMOKE_TIMEOUT="${TILEXR_CCU_SMOKE_TIMEOUT:-120}" + export TILEXR_CCU_PROBE_MISSION_START="${TILEXR_CCU_PROBE_MISSION_START:-6}" + export TILEXR_CCU_PROBE_INSTRUCTION_START="${TILEXR_CCU_PROBE_INSTRUCTION_START:-475}" + export TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START="${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START:-489}" + export TILEXR_CCU_PROBE_SQE_ARG_COUNT="${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-13}" + export TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-143}" + export TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW:-full_repository}" + export TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE:-instruction_bytes}" + export TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE:-acl}" + export TILEXR_CCU_DIRECT_INSTALL_ORDER="${TILEXR_CCU_DIRECT_INSTALL_ORDER:-lower_layer_first}" + export TILEXR_CCU_PROBE_RANK0_XN_START="${TILEXR_CCU_PROBE_RANK0_XN_START:-1961}" + export TILEXR_CCU_PROBE_RANK1_XN_START="${TILEXR_CCU_PROBE_RANK1_XN_START:-1961}" + export TILEXR_CCU_PROBE_GSA_START="${TILEXR_CCU_PROBE_GSA_START:-510}" + export TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START="${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START:-2361}" + export TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START="${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START:-2361}" + export TILEXR_CCU_PROBE_REMOTE_XN_COUNT="${TILEXR_CCU_PROBE_REMOTE_XN_COUNT:-8}" + export TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START:-332}" + export TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START:-332}" + export TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT:-8}" + export TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START:-364}" + export TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START:-364}" + export TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT:-8}" + export TILEXR_CCU_PROBE_CHANNEL_START="${TILEXR_CCU_PROBE_CHANNEL_START:-2}" + if [ "${TILEXR_CCU_DIRECT_BARRIER_MODE:-}" = "" ]; then + export TILEXR_CCU_DIRECT_BARRIER_MODE=sync_cke + else + export TILEXR_CCU_DIRECT_BARRIER_MODE + fi + if [ "${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE:-}" = "" ]; then + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE=hcomm_cap + else + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE + fi + export TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES="${TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES:-prepare,submit,barrier,p2p}" + export TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES="${TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES:-acl,acl_module3,rt_hbm}" + export TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES="${TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES:-}" +} + +prepare_profiles=() +prepare_profile_name="" +prepare_profile_alloc="" +prepare_profile_window="" +prepare_profile_data_len_mode="" +prepare_profile_install_order="" +prepare_profile_pfe_offset_source="" +prepare_profile_pfe_partition="" + +build_prepare_profiles() +{ + prepare_profiles=() + if [ "${TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES:-}" != "" ]; then + IFS=',' read -r -a prepare_profiles <<< "${TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES}" + return + fi + + local prepare_alloc_modes=() + local prepare_mode + IFS=',' read -r -a prepare_alloc_modes <<< "${TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES}" + for prepare_mode in "${prepare_alloc_modes[@]}"; do + prepare_mode="${prepare_mode//[[:space:]]/}" + if [ "${prepare_mode}" = "" ]; then + continue + fi + prepare_profiles+=( + "${prepare_mode}:${prepare_mode}:${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW}:${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE}:${TILEXR_CCU_DIRECT_INSTALL_ORDER}" + ) + done +} + +parse_prepare_profile() +{ + local profile="$1" + local extra1="" + local extra2="" + local extra3="" + IFS=':' read -r \ + prepare_profile_name \ + prepare_profile_alloc \ + prepare_profile_window \ + prepare_profile_data_len_mode \ + prepare_profile_install_order \ + extra1 \ + extra2 \ + extra3 <<< "${profile}" + prepare_profile_name="${prepare_profile_name//[[:space:]]/}" + prepare_profile_alloc="${prepare_profile_alloc//[[:space:]]/}" + prepare_profile_window="${prepare_profile_window//[[:space:]]/}" + prepare_profile_data_len_mode="${prepare_profile_data_len_mode//[[:space:]]/}" + prepare_profile_install_order="${prepare_profile_install_order//[[:space:]]/}" + prepare_profile_pfe_offset_source="${extra1//[[:space:]]/}" + prepare_profile_pfe_partition="${extra2//[[:space:]]/}" + if [ "${prepare_profile_name}" = "" ] || + [ "${prepare_profile_alloc}" = "" ] || + [ "${prepare_profile_window}" = "" ] || + [ "${prepare_profile_data_len_mode}" = "" ] || + [ "${prepare_profile_install_order}" = "" ] || + [ "${extra3}" != "" ]; then + echo "ERROR: invalid prepare profile '${profile}', expected name:alloc:window:dataLenMode:installOrder[:pfeOffsetSource:pfePartition]" >&2 + exit 21 + fi + if { [ "${prepare_profile_pfe_offset_source}" != "" ] && [ "${prepare_profile_pfe_partition}" = "" ]; } || + { [ "${prepare_profile_pfe_offset_source}" = "" ] && [ "${prepare_profile_pfe_partition}" != "" ]; }; then + echo "ERROR: invalid prepare profile '${profile}', pfeOffsetSource and pfePartition must be provided together" >&2 + exit 21 + fi +} + +print_prepare_profile_dry_run() +{ + build_prepare_profiles + local index=0 + local profile + for profile in "${prepare_profiles[@]}"; do + parse_prepare_profile "${profile}" + echo "dryRun prepareProfile[${index}] name=${prepare_profile_name} alloc=${prepare_profile_alloc} window=${prepare_profile_window} dataLenMode=${prepare_profile_data_len_mode} installOrder=${prepare_profile_install_order} pfeOffsetSource=${prepare_profile_pfe_offset_source:-default} pfePartition=${prepare_profile_pfe_partition:-default}" + index=$((index + 1)) + done +} + +print_resource_window_token_dry_run() +{ + local token_field + for token_field in TOKEN_ID RAW_TOKEN_ID TOKEN_VALUE; do + local token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}" + local token_value="${!token_var:-}" + local rank0_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK0" + local rank0_token_value="${!rank0_token_var:-}" + local rank1_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK1" + local rank1_token_value="${!rank1_token_var:-}" + if [ "${token_value}" != "" ]; then + echo "${token_var}=${token_value}" + fi + if [ "${rank0_token_value}" != "" ]; then + echo "${rank0_token_var}=${rank0_token_value}" + fi + if [ "${rank1_token_value}" != "" ]; then + echo "${rank1_token_var}=${rank1_token_value}" + fi + done +} + +run_dry_run() +{ + export TILEXR_CCU_DIRECT_SMOKE_DRY_RUN=1 + echo "tilexr_ccu_direct_evening_smoke dryRun=1" + echo "TILEXR_CCU_SMOKE_DEVICES=${TILEXR_CCU_SMOKE_DEVICES}" + echo "TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE}" + echo "TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT}" + echo "TILEXR_CCU_DIRECT_SMOKE_SUBMIT=${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" + echo "TILEXR_CCU_DIRECT_SMOKE_DRY_RUN=${TILEXR_CCU_DIRECT_SMOKE_DRY_RUN}" + echo "TILEXR_CCU_SMOKE_DRY_RUN=${TILEXR_CCU_SMOKE_DRY_RUN:-0}" + echo "TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES=${TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES}" + echo "TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES=${TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES}" + echo "TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES=${TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES}" + echo "TILEXR_CCU_DIRECT_BARRIER_MODE=${TILEXR_CCU_DIRECT_BARRIER_MODE}" + echo "TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE=${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE}" + echo "TILEXR_CCU_PROBE_MISSION_START=${TILEXR_CCU_PROBE_MISSION_START}" + echo "TILEXR_CCU_PROBE_INSTRUCTION_START=${TILEXR_CCU_PROBE_INSTRUCTION_START}" + echo "TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START=${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START}" + echo "TILEXR_CCU_PROBE_SQE_ARG_COUNT=${TILEXR_CCU_PROBE_SQE_ARG_COUNT}" + echo "TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT=${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT}" + echo "TILEXR_CCU_PROBE_GSA_START=${TILEXR_CCU_PROBE_GSA_START}" + echo "TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE=${TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE}" + echo "TILEXR_CCU_DIRECT_INSTALL_ORDER=${TILEXR_CCU_DIRECT_INSTALL_ORDER}" + print_resource_window_token_dry_run + print_prepare_profile_dry_run + bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +} + +if [ "${TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN:-0}" = "1" ] || + [ "${TILEXR_CCU_SMOKE_DRY_RUN:-0}" = "1" ]; then + set_evening_defaults + run_dry_run + exit 0 +fi + +if [ -f scripts/common_env.sh ]; then + # shellcheck source=/dev/null + source scripts/common_env.sh >/tmp/tilexr_env_evening_smoke.log 2>&1 +fi + +set_evening_defaults +evening_work_root="${TILEXR_CCU_EVENING_WORK_ROOT:-${repo_root}/build/ccu_direct_evening_smoke/$(date +%Y%m%d_%H%M%S)}" + +stage_enabled() +{ + case ",${TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES}," in + *",$1,"*) return 0 ;; + *) return 1 ;; + esac +} + +append_profile_pfe_env() +{ + if [ "${1:-}" = "" ] && [ "${2:-}" = "" ]; then + return + fi + printf '%s\n' \ + "TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE=$1" \ + "TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION=$2" +} + +prepare_has_submit_ready() +{ + local prepare_dir="$1" + local thread_log="${prepare_dir}/ccu_thread.log" + local rank0_log="${prepare_dir}/ccu_rank0.log" + local rank1_log="${prepare_dir}/ccu_rank1.log" + if [ -f "${thread_log}" ]; then + [ "$(grep -c "submitReady=1" "${thread_log}")" -ge 2 ] + return + fi + [ -f "${rank0_log}" ] && [ -f "${rank1_log}" ] && + grep -q "submitReady=1" "${rank0_log}" && + grep -q "submitReady=1" "${rank1_log}" +} + +summarize_stage_logs() +{ + local stage="$1" + local stage_dir="$2" + local status="$3" + local mode="${4:-}" + local found=0 + for log in \ + "${stage_dir}/ccu_thread.log" \ + "${stage_dir}/ccu_rank0.log" \ + "${stage_dir}/ccu_rank1.log"; do + if [ ! -f "${log}" ]; then + continue + fi + found=1 + if [ "${stage}" = "prepare" ]; then + echo "tilexr_ccu_direct_evening_smoke prepareLogSummary mode=${mode} status=${status} log=${log}" + else + echo "tilexr_ccu_direct_evening_smoke stageLogSummary stage=${stage} status=${status} log=${log}" + fi + grep -E 'direct CCU submit failed|rtRet=|args\[' "${log}" | + head -n "${TILEXR_CCU_EVENING_SUBMIT_FAILURE_SUMMARY_LINES:-8}" || true + grep -E \ + 'tilexr_ccu_direct_smoke config|tilexr_ccu_direct_smoke prepare|tilexr_ccu_direct_smoke preparedTasks|tilexr_ccu_direct_smoke submit|tilexr_ccu_direct_smoke submitTiming|tilexr_ccu_direct_smoke p2pCcuCopy|tilexr_ccu_direct_smoke aclrtSynchronizeStream|direct CCU submit failed|rtRet=|args\[|CCU custom channel call failed|op=[0-9]+|driverRet=|opRet=|SET_INSTRUCTION|SET_MSID_TOKEN|submitReady=|TileXRDirectCcuTrace .*decoded=|TileXRDirectCcuTrace remoteXnBinding|TileXRDirectCcuTrace task\[|TileXRDirectCcuTrace finalRuntimeTask|TileXRDirectCcuTrace customChannel.return|TileXRDirectCcuTrace program.sync' \ + "${log}" | tail -n "${TILEXR_CCU_EVENING_LOG_SUMMARY_LINES:-24}" || true + done + if [ "${found}" -eq 0 ]; then + if [ "${stage}" = "prepare" ]; then + echo "tilexr_ccu_direct_evening_smoke prepareLogSummary mode=${mode} status=${status} log=missing workDir=${stage_dir}" + else + echo "tilexr_ccu_direct_evening_smoke stageLogSummary stage=${stage} status=${status} log=missing workDir=${stage_dir}" + fi + fi +} + +extract_last_log_field() +{ + local key="$1" + shift + awk -v key="${key}" ' + { + for (i = 1; i <= NF; ++i) { + if ($i ~ ("^" key "=")) { + split($i, parts, "="); + value = parts[2]; + gsub(/[^0-9A-Za-z_.:-].*$/, "", value); + last = value; + } + } + } + END { + if (last != "") { + print last; + } + } + ' "$@" 2>/dev/null || true +} + +print_prepare_matrix_summary() +{ + local prepare_dir="$1" + local status="$2" + local profile="$3" + local alloc="$4" + local window="$5" + local data_len_mode="$6" + local install_order="$7" + local logs=() + local log + for log in \ + "${prepare_dir}/ccu_thread.log" \ + "${prepare_dir}/ccu_rank0.log" \ + "${prepare_dir}/ccu_rank1.log"; do + if [ -f "${log}" ]; then + logs+=("${log}") + fi + done + + local submit_ready="NA" + local op="NA" + local driver_ret="NA" + local op_ret="NA" + local lower_layer_preconditions=0 + local summary_log="missing" + if [ "${#logs[@]}" -gt 0 ]; then + summary_log="${logs[0]}" + submit_ready="$(extract_last_log_field submitReady "${logs[@]}")" + op="$(extract_last_log_field op "${logs[@]}")" + driver_ret="$(extract_last_log_field driverRet "${logs[@]}")" + op_ret="$(extract_last_log_field opRet "${logs[@]}")" + if grep -q 'lowerLayerPreconditions{' "${logs[@]}"; then + lower_layer_preconditions=1 + fi + fi + submit_ready="${submit_ready:-NA}" + op="${op:-NA}" + driver_ret="${driver_ret:-NA}" + op_ret="${op_ret:-NA}" + + echo "tilexr_ccu_direct_evening_smoke prepareMatrix profile=${profile} status=${status} submitReady=${submit_ready} op=${op} driverRet=${driver_ret} opRet=${op_ret} lowerLayerPreconditions=${lower_layer_preconditions} alloc=${alloc} window=${window} dataLenMode=${data_len_mode} installOrder=${install_order} log=${summary_log}" +} + +run_smoke_stage() +{ + local stage="$1" + shift + local stage_dir="${evening_work_root}/${stage}" + mkdir -p "${stage_dir}" + echo "tilexr_ccu_direct_evening_smoke stage=${stage} workDir=${stage_dir}" + timeout "${TILEXR_CCU_EVENING_TOTAL_TIMEOUT:-160}s" \ + env TILEXR_CCU_SMOKE_WORK_DIR="${stage_dir}" "$@" bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +} + +prepare_stage_exit_is_environmental() +{ + local status="$1" + [ "${status}" -eq 3 ] || [ "${status}" -eq 124 ] +} + +cmake --build build --target tile-comm -j"${TILEXR_CCU_EVENING_BUILD_JOBS:-2}" + +if ! stage_enabled prepare; then + echo "ERROR: TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES must include prepare" >&2 + exit 20 +fi + +build_prepare_profiles +selected_prepare_alloc_mode="" +selected_prepare_window="" +selected_prepare_data_len_mode="" +selected_prepare_install_order="" +selected_prepare_pfe_offset_source="" +selected_prepare_pfe_partition="" +selected_prepare_dir="" +selected_prepare_profile_name="" +prepare_status_summary="" + +print_prepare_failure_final_status() +{ + local final_status_line="tilexr_ccu_direct_evening_smoke finalStatus prepare=fail submit=skipped barrier=skipped p2p=skipped completionCandidate=0 failedStage=prepare selectedProfile=none selectedAlloc=none selectedWindow=none selectedDataLenMode=none selectedInstallOrder=none pfeOffsetSource=default pfePartition=default prepareStatusSummary=${prepare_status_summary}" + echo "${final_status_line}" + mkdir -p "${evening_work_root}" + printf '%s\n' "${final_status_line}" > "${evening_work_root}/final_status.log" +} + +for prepare_profile in "${prepare_profiles[@]}"; do + parse_prepare_profile "${prepare_profile}" + if [ "${prepare_profile_name}" = "" ]; then + continue + fi + safe_prepare_mode="$(printf '%s' "${prepare_profile_name}" | sed 's/[^A-Za-z0-9_]/_/g')" + prepare_status=0 + run_smoke_stage "prepare_${safe_prepare_mode}" \ + TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${prepare_profile_alloc}" \ + TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${prepare_profile_window}" \ + TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${prepare_profile_data_len_mode}" \ + TILEXR_CCU_DIRECT_INSTALL_ORDER="${prepare_profile_install_order}" \ + $(append_profile_pfe_env "${prepare_profile_pfe_offset_source}" "${prepare_profile_pfe_partition}") || + prepare_status=$? + prepare_dir="${evening_work_root}/prepare_${safe_prepare_mode}" + echo "tilexr_ccu_direct_evening_smoke prepareStatus=${prepare_status} profile=${prepare_profile_name} alloc=${prepare_profile_alloc} window=${prepare_profile_window} dataLenMode=${prepare_profile_data_len_mode} installOrder=${prepare_profile_install_order} pfeOffsetSource=${prepare_profile_pfe_offset_source:-default} pfePartition=${prepare_profile_pfe_partition:-default} workDir=${prepare_dir}" + print_prepare_matrix_summary \ + "${prepare_dir}" \ + "${prepare_status}" \ + "${prepare_profile_name}" \ + "${prepare_profile_alloc}" \ + "${prepare_profile_window}" \ + "${prepare_profile_data_len_mode}" \ + "${prepare_profile_install_order}" + summarize_stage_logs prepare "${prepare_dir}" "${prepare_status}" "${prepare_profile_name}" + prepare_status_summary="${prepare_status_summary}${prepare_status_summary:+,}${prepare_profile_name}:${prepare_status}:${prepare_profile_alloc}:${prepare_profile_window}:${prepare_profile_data_len_mode}:${prepare_profile_install_order}:${prepare_profile_pfe_offset_source:-default}:${prepare_profile_pfe_partition:-default}:${prepare_dir}" + if prepare_stage_exit_is_environmental "${prepare_status}"; then + echo "ERROR: direct CCU prepare stopped on environmental gate status=${prepare_status} profile=${prepare_profile_name} alloc=${prepare_profile_alloc} window=${prepare_profile_window} dataLenMode=${prepare_profile_data_len_mode} installOrder=${prepare_profile_install_order} pfeOffsetSource=${prepare_profile_pfe_offset_source:-default} pfePartition=${prepare_profile_pfe_partition:-default} workDir=${prepare_dir}" >&2 + exit "${prepare_status}" + fi + if [ "${prepare_status}" -eq 0 ] && prepare_has_submit_ready "${prepare_dir}"; then + selected_prepare_alloc_mode="${prepare_profile_alloc}" + selected_prepare_window="${prepare_profile_window}" + selected_prepare_data_len_mode="${prepare_profile_data_len_mode}" + selected_prepare_install_order="${prepare_profile_install_order}" + selected_prepare_pfe_offset_source="${prepare_profile_pfe_offset_source}" + selected_prepare_pfe_partition="${prepare_profile_pfe_partition}" + selected_prepare_dir="${prepare_dir}" + selected_prepare_profile_name="${prepare_profile_name}" + break + fi +done + +if [ "${selected_prepare_alloc_mode}" = "" ]; then + print_prepare_failure_final_status + echo 'tilexr_ccu_direct_evening_smoke stopAfter=prepare reason="submitReady=1 missing for every prepare profile" workRoot='"${evening_work_root} prepareStatusSummary=${prepare_status_summary}" + exit 0 +fi +echo "tilexr_ccu_direct_evening_smoke selectedPrepare alloc=${selected_prepare_alloc_mode} window=${selected_prepare_window} dataLenMode=${selected_prepare_data_len_mode} installOrder=${selected_prepare_install_order} pfeOffsetSource=${selected_prepare_pfe_offset_source:-default} pfePartition=${selected_prepare_pfe_partition:-default} workDir=${selected_prepare_dir}" + +submit_final_status="skipped" +barrier_final_status="skipped" +p2p_final_status="skipped" + +print_final_status() +{ + local failed_stage="${1:-none}" + local completion_candidate=0 + if [ "${failed_stage}" = "none" ] && + [ "${submit_final_status}" = "pass" ] && + [ "${barrier_final_status}" = "pass" ] && + [ "${p2p_final_status}" = "pass" ]; then + completion_candidate=1 + fi + + local final_status_line="tilexr_ccu_direct_evening_smoke finalStatus prepare=pass submit=${submit_final_status} barrier=${barrier_final_status} p2p=${p2p_final_status} completionCandidate=${completion_candidate} failedStage=${failed_stage} selectedProfile=${selected_prepare_profile_name} selectedAlloc=${selected_prepare_alloc_mode} selectedWindow=${selected_prepare_window} selectedDataLenMode=${selected_prepare_data_len_mode} selectedInstallOrder=${selected_prepare_install_order} pfeOffsetSource=${selected_prepare_pfe_offset_source:-default} pfePartition=${selected_prepare_pfe_partition:-default}" + echo "${final_status_line}" + mkdir -p "${evening_work_root}" + printf '%s\n' "${final_status_line}" > "${evening_work_root}/final_status.log" +} + +if stage_enabled submit; then + submit_status=0 + run_smoke_stage submit \ + TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}" \ + TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${selected_prepare_window}" \ + TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${selected_prepare_data_len_mode}" \ + TILEXR_CCU_DIRECT_INSTALL_ORDER="${selected_prepare_install_order}" \ + $(append_profile_pfe_env "${selected_prepare_pfe_offset_source}" "${selected_prepare_pfe_partition}") \ + TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 || + submit_status=$? + summarize_stage_logs submit "${evening_work_root}/submit" "${submit_status}" + if [ "${submit_status}" -ne 0 ]; then + submit_final_status="fail" + print_final_status submit + exit "${submit_status}" + fi + submit_final_status="pass" +fi + +if stage_enabled barrier; then + barrier_status=0 + run_smoke_stage barrier \ + TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}" \ + TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${selected_prepare_window}" \ + TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${selected_prepare_data_len_mode}" \ + TILEXR_CCU_DIRECT_INSTALL_ORDER="${selected_prepare_install_order}" \ + $(append_profile_pfe_env "${selected_prepare_pfe_offset_source}" "${selected_prepare_pfe_partition}") \ + TILEXR_CCU_DIRECT_BARRIER_MODE="${TILEXR_CCU_DIRECT_BARRIER_MODE}" \ + TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ + TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK="${TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK:-0}" \ + TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS="${TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS:-300}" \ + TILEXR_CCU_DIRECT_SMOKE_EXPECT_BARRIER_WAIT=1 \ + TILEXR_CCU_DIRECT_SMOKE_MIN_SYNC_MS="${TILEXR_CCU_DIRECT_SMOKE_MIN_SYNC_MS:-100}" || + barrier_status=$? + summarize_stage_logs barrier "${evening_work_root}/barrier" "${barrier_status}" + if [ "${barrier_status}" -ne 0 ]; then + barrier_final_status="fail" + print_final_status barrier + exit "${barrier_status}" + fi + barrier_final_status="pass" +fi + +if stage_enabled p2p; then + p2p_status=0 + run_smoke_stage p2p \ + TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}" \ + TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${selected_prepare_window}" \ + TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${selected_prepare_data_len_mode}" \ + TILEXR_CCU_DIRECT_INSTALL_ORDER="${selected_prepare_install_order}" \ + $(append_profile_pfe_env "${selected_prepare_pfe_offset_source}" "${selected_prepare_pfe_partition}") \ + TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=1 \ + TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ + TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY=1 \ + TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY=1 || + p2p_status=$? + summarize_stage_logs p2p "${evening_work_root}/p2p" "${p2p_status}" + if [ "${p2p_status}" -ne 0 ]; then + p2p_final_status="fail" + print_final_status p2p + exit "${p2p_status}" + fi + p2p_final_status="pass" +fi + +print_final_status none +echo "tilexr_ccu_direct_evening_smoke success workRoot=${evening_work_root}" diff --git a/tests/ccu/run_tilexr_ccu_direct_smoke.sh b/tests/ccu/run_tilexr_ccu_direct_smoke.sh new file mode 100644 index 0000000..d48e1db --- /dev/null +++ b/tests/ccu/run_tilexr_ccu_direct_smoke.sh @@ -0,0 +1,609 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2026 TileXR Project +# +# Two-rank runner for the private TileXR direct CCU smoke probe. +# Default execution is safe and does not touch ACL/NPU runtime. + +set -euo pipefail + +if [ "${TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE:-0}" != "1" ]; then + echo "tilexr_ccu_direct_smoke_runner skipped: set TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 to run hardware smoke" + exit 0 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "${repo_root}" + +ASCEND_HOME_PATH="${ASCEND_HOME_PATH:-/usr/local/Ascend/ascend-toolkit/latest}" +ARCH="${ARCH:-$(uname -m)}" +if [ "${ARCH}" = "arm64" ]; then + ARCH="aarch64" +fi +ASCEND_DRIVER_PATH="${ASCEND_DRIVER_PATH:-/usr/local/Ascend/driver}" +export ASCEND_HOME_PATH ARCH ASCEND_DRIVER_PATH +export TILEXR_CCU_DIRECT_INSTALL_ORDER="${TILEXR_CCU_DIRECT_INSTALL_ORDER:-lower_layer_first}" + +work_dir="${TILEXR_CCU_SMOKE_WORK_DIR:-${repo_root}/build/ccu_direct_smoke}" +mkdir -p "${work_dir}" + +endpoint_fields=( + EID + TPN + DOORBELL_VA + DOORBELL_TOKEN_ID + DOORBELL_TOKEN_VALUE + SQ_DEPTH +) + +resource_window_token_fields=( + TOKEN_ID + RAW_TOKEN_ID + TOKEN_VALUE +) + +parse_int() +{ + local value="$1" + local fallback="$2" + if [ -z "${value}" ]; then + echo "${fallback}" + return + fi + printf "%d" "${value}" 2>/dev/null || printf "%d" "${fallback}" +} + +default_sync_instruction_count() +{ + local sync_resource_count="$1" + local barrier_mode="${TILEXR_CCU_DIRECT_BARRIER_MODE:-}" + local hcomm_style_task1_prelude_count=5 + case "${barrier_mode}" in + sync_cke|sync_cke_set_wait) + echo $((sync_resource_count * 2 + 1)) + ;; + sync_cke_post_only) + echo $((sync_resource_count + 1)) + ;; + local_cke_post_only) + echo "${sync_resource_count}" + ;; + sync_xn_post_only) + echo $((hcomm_style_task1_prelude_count + sync_resource_count)) + ;; + sync_xn_load_post_only) + echo $((hcomm_style_task1_prelude_count + sync_resource_count * 2)) + ;; + *) + echo $((hcomm_style_task1_prelude_count + sync_resource_count * 2)) + ;; + esac +} + +if [ "${TILEXR_CCU_DIRECT_SMOKE_DRY_RUN:-0}" = "1" ]; then + echo "tilexr_ccu_direct_smoke_runner dryRun=1 workDir=${work_dir}" + for diagnostic_var in \ + TILEXR_CCU_DIRECT_BARRIER_MODE \ + TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE \ + TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW \ + TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE \ + TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE \ + TILEXR_CCU_DIRECT_INSTALL_ORDER \ + TILEXR_CCU_PROBE_SQE_ARG_COUNT \ + TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START; do + diagnostic_value="${!diagnostic_var:-}" + if [ "${diagnostic_value}" != "" ]; then + echo "dryRun ${diagnostic_var}=${diagnostic_value}" + fi + done + sqe_arg_count="$(parse_int "${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-13}" 13)" + sync_resource_count="$(parse_int "${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-1}" 1)" + default_sync_instruction_count_value="$(default_sync_instruction_count "${sync_resource_count}")" + sync_instruction_count="$(parse_int \ + "${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-${default_sync_instruction_count_value}}" \ + "${default_sync_instruction_count_value}")" + repository_start="$(parse_int "${TILEXR_CCU_PROBE_INSTRUCTION_START:-1}" 1)" + mission_instruction_start="$(parse_int "${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START:-0}" 0)" + if [ "${mission_instruction_start}" -eq 0 ]; then + mission_instruction_start="${repository_start}" + fi + repository_prefix_count=$((mission_instruction_start - repository_start)) + if [ "${repository_prefix_count}" -lt 0 ]; then + repository_prefix_count=0 + fi + mission_instruction_count=$((sqe_arg_count + sync_instruction_count)) + repository_count=$((repository_prefix_count + mission_instruction_count)) + task0_start="${mission_instruction_start}" + task0_count="${sqe_arg_count}" + task1_start=$((mission_instruction_start + sqe_arg_count)) + task1_count="${sync_instruction_count}" + if [ "${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW:-}" = "full_repository" ] || + [ "${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW:-}" = "full" ] || + [ "${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW:-}" = "1" ]; then + install_start="${repository_start}" + install_count="${repository_count}" + else + install_start="${mission_instruction_start}" + install_count="${mission_instruction_count}" + fi + instruction_data_len=$((install_count * 32)) + if [ "${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE:-}" = "descriptor_bytes" ] || + [ "${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE:-}" = "descriptor" ] || + [ "${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE:-}" = "1" ]; then + set_instruction_data_len=24 + else + set_instruction_data_len="${instruction_data_len}" + fi + echo "dryRun derived repositoryStartId=${repository_start} repositoryCount=${repository_count} missionInstructionStartId=${mission_instruction_start} missionInstructionCount=${mission_instruction_count}" + echo "dryRun derived task0.instStartId=${task0_start} task0.instCnt=${task0_count}" + echo "dryRun derived task1.instStartId=${task1_start} task1.instCnt=${task1_count}" + echo "dryRun derived SET_INSTRUCTION offsetStartIdx=${install_start} dataLen=${set_instruction_data_len} instructionBytes=${instruction_data_len}" + for endpoint_field in "${endpoint_fields[@]}"; do + endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}" + common_endpoint_value="${!endpoint_var:-}" + rank0_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK0" + rank0_endpoint_value="${!rank0_endpoint_var:-${common_endpoint_value}}" + rank1_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK1" + rank1_endpoint_value="${!rank1_endpoint_var:-${common_endpoint_value}}" + if [ "${rank0_endpoint_value}" != "" ]; then + echo "dryRun rank0 TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank0_endpoint_value}" + fi + if [ "${rank1_endpoint_value}" != "" ]; then + echo "dryRun rank1 TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank1_endpoint_value}" + fi + done + for token_field in "${resource_window_token_fields[@]}"; do + token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}" + common_token_value="${!token_var:-}" + rank0_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK0" + rank0_token_value="${!rank0_token_var:-${common_token_value}}" + rank1_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK1" + rank1_token_value="${!rank1_token_var:-${common_token_value}}" + if [ "${rank0_token_value}" != "" ]; then + echo "dryRun rank0 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}=${rank0_token_value}" + fi + if [ "${rank1_token_value}" != "" ]; then + echo "dryRun rank1 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}=${rank1_token_value}" + fi + done + exit 0 +fi + +tile_comm_lib="${TILEXR_TILE_COMM_LIB:-}" +if [ -z "${tile_comm_lib}" ]; then + for candidate in \ + "${repo_root}/build/src/comm/libtile-comm.so" \ + "${repo_root}/install/lib64/libtile-comm.so" \ + "${repo_root}/install/lib/libtile-comm.so" \ + "${repo_root}/install_direct_ccu_guard/lib64/libtile-comm.so"; do + if [ -f "${candidate}" ]; then + tile_comm_lib="${candidate}" + break + fi + done +fi +if [ -z "${tile_comm_lib}" ] || [ ! -f "${tile_comm_lib}" ]; then + echo "ERROR: libtile-comm.so not found; build tile-comm first or set TILEXR_TILE_COMM_LIB" >&2 + exit 2 +fi +tile_comm_dir="$(cd "$(dirname "${tile_comm_lib}")" && pwd)" + +cann_root="${ASCEND_HOME_PATH}/${ARCH}-linux" +cann_lib_dir="${cann_root}/lib64" +driver_lib_dir="${ASCEND_DRIVER_PATH}/lib64/driver" +probe_bin="${work_dir}/ccu_tilexr_direct_smoke_probe" + +c++ -std=c++14 \ + -I "${repo_root}/src/include" \ + -I "${repo_root}/src/comm" \ + -I "${cann_root}/pkg_inc" \ + -I "${cann_root}/pkg_inc/runtime" \ + -I "${cann_root}/include" \ + "${repo_root}/tests/ccu/ccu_tilexr_direct_smoke_probe.cpp" \ + -L "${tile_comm_dir}" \ + -L "${cann_lib_dir}" \ + -L "${driver_lib_dir}" \ + -Wl,-rpath-link,"${tile_comm_dir}" \ + -Wl,-rpath-link,"${cann_lib_dir}" \ + -Wl,-rpath-link,"${driver_lib_dir}" \ + -ltile-comm -lascendcl -lruntime -ldl -pthread \ + -o "${probe_bin}" + +devices="${TILEXR_CCU_SMOKE_DEVICES:-${TILEXR_TEST_DEVICES:-0,1}}" + +if command -v npu-smi >/dev/null 2>&1; then + npu_smi_rc=0 + timeout "${TILEXR_CCU_SMOKE_NPU_SMI_TIMEOUT:-20}s" npu-smi info > "${work_dir}/npu-smi.log" 2>&1 || npu_smi_rc=$? + if [ "${npu_smi_rc}" -ne 0 ]; then + echo "ERROR: npu-smi info did not complete; refusing to run ACL/CCU smoke" >&2 + echo "npu-smi rc=${npu_smi_rc}" >&2 + echo "npu-smi log: ${work_dir}/npu-smi.log" >&2 + exit 3 + fi + if [ "${TILEXR_CCU_SMOKE_ALLOW_BUSY_NPU:-0}" != "1" ]; then + busy_rc=0 + npu_guard_args=( + --log "${work_dir}/npu-smi.log" + --devices "${devices}" + ) + if [ "${TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU:-0}" = "1" ]; then + npu_guard_args+=(--allow-unhealthy) + fi + python3 "${repo_root}/tests/ccu/ccu_npu_smi_busy_guard.py" \ + "${npu_guard_args[@]}" > "${work_dir}/npu-smi-busy.log" 2>&1 || busy_rc=$? + if [ "${busy_rc}" -ne 0 ]; then + echo "ERROR: selected NPU device is busy or unhealthy; refusing to run ACL/CCU smoke" >&2 + cat "${work_dir}/npu-smi-busy.log" >&2 + echo "npu-smi log: ${work_dir}/npu-smi.log" >&2 + echo "set TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU=1 to allow Alarm health while still rejecting busy devices" >&2 + echo "set TILEXR_CCU_SMOKE_ALLOW_BUSY_NPU=1 only for an explicitly approved short test that may use busy devices" >&2 + exit 3 + fi + fi +elif [ "${TILEXR_CCU_SMOKE_REQUIRE_NPU_SMI:-0}" = "1" ]; then + echo "ERROR: npu-smi not found; refusing to run ACL/CCU smoke" >&2 + exit 3 +fi + +comm_port="${TILEXR_CCU_SMOKE_PORT:-$((30000 + (RANDOM % 20000)))}" +comm_id="${TILEXR_COMM_ID:-127.0.0.1:${comm_port}}" +comm_domain="${TILEXR_CCU_PROBE_COMM_DOMAIN:-0}" +timeout_s="${TILEXR_CCU_SMOKE_TIMEOUT:-180}" +ready_dir="${work_dir}/submit_ready_${comm_port}" +done_dir="${work_dir}/submit_done_${comm_port}" +rank0_log="${work_dir}/ccu_rank0.log" +rank1_log="${work_dir}/ccu_rank1.log" +rm -rf "${ready_dir}" "${done_dir}" +mkdir -p "${ready_dir}" "${done_dir}" +rm -f "${rank0_log}" "${rank1_log}" + +common_env=( + "LD_LIBRARY_PATH=${tile_comm_dir}:${cann_lib_dir}:${driver_lib_dir}:${LD_LIBRARY_PATH:-}" + "TILEXR_COMM_ID=${comm_id}" + "TILEXR_TEST_DEVICES=${devices}" + "TILEXR_CCU_DIRECT_SMOKE_ENABLE=1" + "TILEXR_CCU_DIRECT_SMOKE_READY_DIR=${ready_dir}" + "TILEXR_CCU_DIRECT_SMOKE_DONE_DIR=${done_dir}" + "TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE=${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE:-1}" + "TILEXR_CCU_PROBE_RANK_SIZE=2" + "TILEXR_CCU_PROBE_COMM_DOMAIN=${comm_domain}" +) +if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE:-0}" = "1" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=1") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-0}" = "1" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=1") +fi +if [ "${TILEXR_CCU_DIRECT_BARRIER_MODE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_BARRIER_MODE=${TILEXR_CCU_DIRECT_BARRIER_MODE}") +fi +if [ "${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW=${TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW}") +fi +if [ "${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE=${TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE}") +fi +if [ "${TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE=${TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE}") +fi +if [ "${TILEXR_CCU_DIRECT_INSTALL_ORDER:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_INSTALL_ORDER=${TILEXR_CCU_DIRECT_INSTALL_ORDER}") +fi +if [ "${TILEXR_CCU_DIRECT_INSTALL_DIE_ID:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_INSTALL_DIE_ID=${TILEXR_CCU_DIRECT_INSTALL_DIE_ID}") +fi +if [ "${TILEXR_CCU_PROBE_MISSION_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_MISSION_START=${TILEXR_CCU_PROBE_MISSION_START}") +fi +if [ "${TILEXR_CCU_PROBE_INSTRUCTION_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_INSTRUCTION_START=${TILEXR_CCU_PROBE_INSTRUCTION_START}") +fi +if [ "${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START=${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START}") +fi +if [ "${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_SQE_ARG_COUNT=${TILEXR_CCU_PROBE_SQE_ARG_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT=${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT=${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_BINDINGS_PER_RESOURCE:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_BINDINGS_PER_RESOURCE=${TILEXR_CCU_PROBE_BINDINGS_PER_RESOURCE}") +fi +if [ "${TILEXR_CCU_PROBE_CKE_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_CKE_START=${TILEXR_CCU_PROBE_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_GSA_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_GSA_START=${TILEXR_CCU_PROBE_GSA_START}") +fi +if [ "${TILEXR_CCU_PROBE_CHANNEL_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_CHANNEL_START=${TILEXR_CCU_PROBE_CHANNEL_START}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK=${TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS=${TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES}") +fi +if [ "${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START=${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT=${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START=${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT=${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT}") +fi +for endpoint_field in "${endpoint_fields[@]}"; do + endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}" + endpoint_value="${!endpoint_var:-}" + if [ "${endpoint_value}" != "" ]; then + common_env+=("${endpoint_var}=${endpoint_value}") + fi +done +for token_field in "${resource_window_token_fields[@]}"; do + token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}" + token_value="${!token_var:-}" + if [ "${token_value}" != "" ]; then + common_env+=("${token_var}=${token_value}") + fi + rank0_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK0" + rank0_token_value="${!rank0_token_var:-}" + if [ "${rank0_token_value}" != "" ]; then + common_env+=("${rank0_token_var}=${rank0_token_value}") + fi + rank1_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK1" + rank1_token_value="${!rank1_token_var:-}" + if [ "${rank1_token_value}" != "" ]; then + common_env+=("${rank1_token_var}=${rank1_token_value}") + fi +done + +rank0_env=() +rank1_env=() +if [ "${TILEXR_CCU_PROBE_RANK0_XN_START:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_XN_START=${TILEXR_CCU_PROBE_RANK0_XN_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_XN_START:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_XN_START=${TILEXR_CCU_PROBE_RANK1_XN_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_REMOTE_XN_START=${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_REMOTE_XN_START=${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_COUNT:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_REMOTE_XN_COUNT=${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_COUNT}") +elif [ "${TILEXR_CCU_PROBE_REMOTE_XN_COUNT:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_REMOTE_XN_COUNT=${TILEXR_CCU_PROBE_REMOTE_XN_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_COUNT:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_REMOTE_XN_COUNT=${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_COUNT}") +elif [ "${TILEXR_CCU_PROBE_REMOTE_XN_COUNT:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_REMOTE_XN_COUNT=${TILEXR_CCU_PROBE_REMOTE_XN_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START=${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START=${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_COUNT:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT=${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_COUNT:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT=${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START=${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START=${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START}") +fi +if [ "${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_COUNT:-}" != "" ]; then + rank0_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT=${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_COUNT}") +fi +if [ "${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_COUNT:-}" != "" ]; then + rank1_env+=("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT=${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_COUNT}") +fi +for endpoint_field in "${endpoint_fields[@]}"; do + rank0_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK0" + rank0_endpoint_value="${!rank0_endpoint_var:-}" + if [ "${rank0_endpoint_value}" != "" ]; then + rank0_env+=("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank0_endpoint_value}") + fi + rank1_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK1" + rank1_endpoint_value="${!rank1_endpoint_var:-}" + if [ "${rank1_endpoint_value}" != "" ]; then + rank1_env+=("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank1_endpoint_value}") + fi +done +for token_field in "${resource_window_token_fields[@]}"; do + rank0_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK0" + rank0_token_value="${!rank0_token_var:-}" + if [ "${rank0_token_value}" != "" ]; then + rank0_env+=("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}=${rank0_token_value}") + fi + rank1_token_var="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}_RANK1" + rank1_token_value="${!rank1_token_var:-}" + if [ "${rank1_token_value}" != "" ]; then + rank1_env+=("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}=${rank1_token_value}") + fi +done + +echo "tilexr_ccu_direct_smoke_runner begin workDir=${work_dir} devices=${devices} commId=${comm_id} threadMode=${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE:-0} submit=${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0} barrierMode=${TILEXR_CCU_DIRECT_BARRIER_MODE:-} p2pCcuCopy=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY:-0} timeout=${timeout_s} npuSmiTimeout=${TILEXR_CCU_SMOKE_NPU_SMI_TIMEOUT:-20}" + +if [ "${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE:-0}" = "1" ]; then + thread_log="${work_dir}/ccu_thread.log" + rm -f "${thread_log}" + thread_status=0 + timeout "${timeout_s}s" env "${common_env[@]}" TILEXR_CCU_PROBE_RANK=0 "${probe_bin}" > "${thread_log}" 2>&1 || + thread_status=$? + cat "${thread_log}" + echo "tilexr_ccu_direct_smoke_runner threadMode summary status=${thread_status} log=${thread_log}" + if [ "${thread_status}" -ne 0 ]; then + echo "ERROR: direct CCU thread-mode smoke failed status=${thread_status}" >&2 + echo "thread log: ${thread_log}" >&2 + exit 4 + fi + if [ "$(grep -c "tilexr_ccu_direct_smoke prepare ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode prepare did not return success for both ranks" >&2 + exit 5 + fi + if [ "$(grep -c "installSucceeded=1" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode prepare did not complete install attempt for both ranks" >&2 + exit 6 + fi + if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]; then + if [ "$(grep -c "submitReady=1" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode submit requested but prepare did not reach submitReady=1" >&2 + exit 6 + fi + if [ "$(grep -c "tilexr_ccu_direct_smoke submit ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode submit did not return success for both ranks" >&2 + exit 7 + fi + if [ "$(grep -c "tilexr_ccu_direct_smoke submitTiming" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode submit timing was not reported for both ranks" >&2 + exit 8 + fi + fi + if [ "${TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY:-0}" = "1" ]; then + if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" != "1" ]; then + echo "ERROR: direct CCU thread-mode P2P CCU-copy check requires TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1" >&2 + exit 11 + fi + if [ "$(grep -c "tilexr_ccu_direct_smoke p2pCcuCopy" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode P2P CCU-copy result missing" >&2 + exit 12 + fi + if [ "$(grep -c "tilexr_ccu_direct_smoke p2pCcuCopy .*passed=1" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU thread-mode P2P CCU-copy check failed" >&2 + exit 13 + fi + fi + echo "tilexr_ccu_direct_smoke_runner success workDir=${work_dir}" + exit 0 +fi + +timeout "${timeout_s}s" env "${common_env[@]}" "${rank0_env[@]}" TILEXR_CCU_PROBE_RANK=0 "${probe_bin}" > "${rank0_log}" 2>&1 & +rank0_pid=$! +sleep "${TILEXR_CCU_SMOKE_RANK1_DELAY:-1}" +timeout "${timeout_s}s" env "${common_env[@]}" "${rank1_env[@]}" TILEXR_CCU_PROBE_RANK=1 "${probe_bin}" > "${rank1_log}" 2>&1 & +rank1_pid=$! + +rank0_status=0 +rank1_status=0 +wait "${rank0_pid}" || rank0_status=$? +wait "${rank1_pid}" || rank1_status=$? + +cat "${rank0_log}" +cat "${rank1_log}" + +echo "tilexr_ccu_direct_smoke_runner summary rank0Status=${rank0_status} rank1Status=${rank1_status} rank0Log=${rank0_log} rank1Log=${rank1_log}" + +if [ "${rank0_status}" -ne 0 ] || [ "${rank1_status}" -ne 0 ]; then + echo "ERROR: direct CCU smoke rank process failed rank0=${rank0_status} rank1=${rank1_status}" >&2 + echo "rank0 log: ${rank0_log}" >&2 + echo "rank1 log: ${rank1_log}" >&2 + exit 4 +fi + +for log in "${rank0_log}" "${rank1_log}"; do + if ! grep -q "tilexr_ccu_direct_smoke prepare ret=0" "${log}"; then + echo "ERROR: direct CCU prepare did not return success in ${log}" >&2 + exit 5 + fi + if ! grep -q "installSucceeded=1" "${log}"; then + echo "ERROR: direct CCU prepare did not complete install attempt in ${log}" >&2 + exit 6 + fi +done + +if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]; then + for log in "${rank0_log}" "${rank1_log}"; do + if ! grep -q "submitReady=1" "${log}"; then + echo "ERROR: direct CCU submit requested but prepare did not reach submitReady=1 in ${log}" >&2 + exit 6 + fi + done + for log in "${rank0_log}" "${rank1_log}"; do + if ! grep -q "tilexr_ccu_direct_smoke submit ret=0" "${log}"; then + echo "ERROR: direct CCU submit did not return success in ${log}" >&2 + exit 7 + fi + if ! grep -q "tilexr_ccu_direct_smoke submitTiming" "${log}"; then + echo "ERROR: direct CCU submit timing was not reported in ${log}" >&2 + exit 8 + fi + done +fi + +if [ "${TILEXR_CCU_DIRECT_SMOKE_EXPECT_BARRIER_WAIT:-0}" = "1" ]; then + if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" != "1" ]; then + echo "ERROR: direct CCU barrier wait check requires TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1" >&2 + exit 9 + fi + delay_rank="${TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK:-0}" + min_sync_ms="${TILEXR_CCU_DIRECT_SMOKE_MIN_SYNC_MS:-100}" + if [ "${delay_rank}" = "0" ]; then + wait_log="${rank1_log}" + else + wait_log="${rank0_log}" + fi + wait_sync_ms="$( + awk ' + /tilexr_ccu_direct_smoke submitTiming/ { + for (i = 1; i <= NF; ++i) { + if ($i ~ /^syncMs=/) { + split($i, parts, "="); + print parts[2]; + } + } + } + ' "${wait_log}" | tail -n 1 + )" + if [ -z "${wait_sync_ms}" ]; then + echo "ERROR: barrier wait timing missing from ${wait_log}" >&2 + exit 9 + fi + if [ "${wait_sync_ms}" -lt "${min_sync_ms}" ]; then + echo "ERROR: direct CCU barrier wait was too short syncMs=${wait_sync_ms} minSyncMs=${min_sync_ms} log=${wait_log}" >&2 + exit 10 + fi +fi + +if [ "${TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY:-0}" = "1" ]; then + if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" != "1" ]; then + echo "ERROR: direct CCU P2P CCU-copy check requires TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1" >&2 + exit 11 + fi + for log in "${rank0_log}" "${rank1_log}"; do + if ! grep -q "tilexr_ccu_direct_smoke p2pCcuCopy" "${log}"; then + echo "ERROR: direct CCU P2P CCU-copy result missing in ${log}" >&2 + exit 12 + fi + if ! grep -q "tilexr_ccu_direct_smoke p2pCcuCopy .*passed=1" "${log}"; then + echo "ERROR: direct CCU P2P CCU-copy check failed in ${log}" >&2 + exit 13 + fi + done +fi + +echo "tilexr_ccu_direct_smoke_runner success workDir=${work_dir}" diff --git a/tests/ccu/test_tilexr_ccu_barrier_program.py b/tests/ccu/test_tilexr_ccu_barrier_program.py new file mode 100644 index 0000000..254a1b8 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_barrier_program.py @@ -0,0 +1,649 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +BARRIER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.h" +BARRIER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +PRODUCER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + #include + + using namespace TileXR; + + int main() + { + std::vector specs; + specs.push_back({2361, 1961, 2, 364, 1, 332, 1}); + specs.push_back({2362, 1962, 3, 365, 1, 333, 1}); + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram(specs, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "barrier build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 4 || report.postInstructionCount != 2 || + report.waitInstructionCount != 2 || report.totalInstructionCount != 4 || + report.message != "ok") { + std::cerr << "unexpected barrier report\n"; + return 2; + } + if (program[0].words[0] != 0x000007a90939100dULL || + program[0].words[1] != 0x00000001016c0002ULL || + program[0].words[2] != 0x0001000000000000ULL || + program[0].words[3] != 0) { + std::cerr << "unexpected first post instruction\n"; + return 3; + } + if (program[1].words[0] != 0x000007aa093a100dULL || + program[1].words[1] != 0x00000001016d0003ULL || + program[1].words[2] != 0x0001000000000000ULL || + program[1].words[3] != 0) { + std::cerr << "unexpected second post instruction\n"; + return 4; + } + if (program[2].words[0] != 0x0000000000010802ULL || + program[2].words[1] != 0x000000000001014cULL || + program[2].words[2] != 0 || + program[2].words[3] != 0) { + std::cerr << "unexpected first wait/clear instruction\n"; + return 5; + } + if (program[3].words[0] != 0x0000000000010802ULL || + program[3].words[1] != 0x000000000001014dULL || + program[3].words[2] != 0 || + program[3].words[3] != 0) { + std::cerr << "unexpected second wait/clear instruction\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_rejects_incomplete_resource_bindings(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + std::vector program(1); + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram({}, &program, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty barrier specs accepted\n"; + return 1; + } + if (!program.empty() || report.message.find("missing") == std::string::npos) { + std::cerr << "empty barrier diagnostic/report mismatch: " << report.message << "\n"; + return 2; + } + + std::vector specs; + specs.push_back({2361, 1961, 2, 364, 1, 0, 1}); + if (TileXRCcuBuildBarrierProgram(specs, &program, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing local wait CKE accepted\n"; + return 3; + } + if (!program.empty() || report.message.find("local wait CKE") == std::string::npos) { + std::cerr << "weak missing local wait diagnostic: " << report.message << "\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_emit_hcomm_like_synccke_post_and_clear_wait(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.channelId = 2; + spec.remoteNotifyCke = 0x330; + spec.remoteNotifyMask = 1; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + spec.sourceCke = 0x221; + spec.sourceCkeMask = 0xffff; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::SyncCke) != TILEXR_SUCCESS) { + std::cerr << "sync_cke barrier build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 3 || report.postInstructionCount != 1 || + report.waitInstructionCount != 1 || report.totalInstructionCount != 3) { + std::cerr << "unexpected sync_cke barrier report\n"; + return 2; + } + if (program[0].words[0] != 0xffff022100010802ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "source CKE init mismatch\n"; + return 3; + } + if (program[1].words[0] != 0x000102210330100bULL || + program[1].words[1] != 0x0000000000000002ULL || + program[1].words[2] != 0x0001000000000000ULL || + program[1].words[3] != 0) { + std::cerr << "SyncCKE post mismatch\n"; + return 4; + } + if (program[2].words[0] != 0x0000000000010804ULL || + program[2].words[1] != 0x0000000000010220ULL || + program[2].words[2] != 0 || + program[2].words[3] != 0) { + std::cerr << "ClearCKE wait mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_emit_synccke_post_and_set_wait(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.channelId = 2; + spec.remoteNotifyCke = 0x330; + spec.remoteNotifyMask = 1; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + spec.sourceCke = 0x221; + spec.sourceCkeMask = 0xffff; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::SyncCkeSetWait) != TILEXR_SUCCESS) { + std::cerr << "sync_cke_set_wait barrier build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 3 || report.postInstructionCount != 1 || + report.waitInstructionCount != 1 || report.totalInstructionCount != 3) { + std::cerr << "unexpected sync_cke_set_wait barrier report\n"; + return 2; + } + if (program[0].words[0] != 0xffff022100010802ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "source CKE init mismatch\n"; + return 3; + } + if (program[1].words[0] != 0x000102210330100bULL || + program[1].words[1] != 0x0000000000000002ULL || + program[1].words[2] != 0x0001000000000000ULL || + program[1].words[3] != 0) { + std::cerr << "SyncCKE post mismatch\n"; + return 4; + } + if (program[2].words[0] != 0x0000000000010802ULL || + program[2].words[1] != 0x0000000000010220ULL || + program[2].words[2] != 0 || + program[2].words[3] != 0) { + std::cerr << "SetCKE wait mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_emit_synccke_post_only_diagnostic(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.channelId = 2; + spec.remoteNotifyCke = 0x330; + spec.remoteNotifyMask = 1; + spec.sourceCke = 0x221; + spec.sourceCkeMask = 0xffff; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::SyncCkePostOnly) != TILEXR_SUCCESS) { + std::cerr << "sync_cke_post_only barrier build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 2 || report.postInstructionCount != 1 || + report.waitInstructionCount != 0 || report.totalInstructionCount != 2) { + std::cerr << "unexpected sync_cke_post_only barrier report\n"; + return 2; + } + if (program[0].words[0] != 0xffff022100010802ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "source CKE init mismatch\n"; + return 3; + } + if (program[1].words[0] != 0x000102210330100bULL || + program[1].words[1] != 0x0000000000000002ULL || + program[1].words[2] != 0x0001000000000000ULL || + program[1].words[3] != 0) { + std::cerr << "SyncCKE post-only mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_emit_local_cke_completion_diagnostic(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::LocalCke) != TILEXR_SUCCESS) { + std::cerr << "local CKE diagnostic build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 2 || report.postInstructionCount != 1 || + report.waitInstructionCount != 1 || report.totalInstructionCount != 2) { + std::cerr << "unexpected local CKE diagnostic report\n"; + return 2; + } + if (program[0].words[0] != 0x0001022000000802ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "local CKE set mismatch\n"; + return 3; + } + if (program[1].words[0] != 0x0000000000010804ULL || + program[1].words[1] != 0x0000000000010220ULL || + program[1].words[2] != 0 || + program[1].words[3] != 0) { + std::cerr << "local CKE clear/wait mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_emit_local_cke_post_only_diagnostic(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::LocalCkePostOnly) != TILEXR_SUCCESS) { + std::cerr << "local CKE post-only diagnostic build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 1 || report.postInstructionCount != 1 || + report.waitInstructionCount != 0 || report.totalInstructionCount != 1) { + std::cerr << "unexpected local CKE post-only diagnostic report\n"; + return 2; + } + if (program[0].words[0] != 0x0001022000000802ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "local CKE post-only set mismatch\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_can_load_local_xn_before_sync_xn_post_only_diagnostic(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_barrier_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBarrierSyncSpec spec; + spec.remoteXn = 0x240; + spec.localXn = 0x120; + spec.channelId = 2; + spec.remoteNotifyCke = 0x330; + spec.remoteNotifyMask = 1; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildBarrierProgram( + std::vector{spec}, + &program, + &report, + TileXRCcuBarrierMode::SyncXnLoadPostOnly) != TILEXR_SUCCESS) { + std::cerr << "sync_xn_load_post_only diagnostic build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 2 || report.postInstructionCount != 1 || + report.waitInstructionCount != 0 || report.totalInstructionCount != 2) { + std::cerr << "unexpected sync_xn_load_post_only report\n"; + return 2; + } + if (program[0].words[0] != 0x0000000101200003ULL || + program[0].words[1] != 0 || + program[0].words[2] != 0 || + program[0].words[3] != 0) { + std::cerr << "local XN load immediate mismatch\n"; + return 3; + } + if (program[1].words[0] != 0x000001200240100dULL || + program[1].words[1] != 0x0000000103300002ULL || + program[1].words[2] != 0x0001000000000000ULL || + program[1].words[3] != 0) { + std::cerr << "SyncXn post mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_producer_microcode_uses_barrier_post_and_wait_program(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_producer_plan.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 3}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 332, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 333, 3, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuProgram program; + TileXRCcuProducerPlanReport report; + if (TileXRCcuBuildMicrocode(plan, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << report.message << "\n"; + return 1; + } + if (program.sqeLoad.size() != 13 || program.sync.size() != 9) { + std::cerr << "unexpected producer barrier microcode size\n"; + return 2; + } + if (program.sync[5].words[0] != 0x000007a90939100dULL || + program.sync[5].words[1] != 0x00000001014c0002ULL || + program.sync[7].words[0] != 0x0000000000010802ULL || + program.sync[7].words[1] != 0x000000000001014cULL) { + std::cerr << "unexpected first producer barrier pair\n"; + return 3; + } + if (program.sync[6].words[0] != 0x000007aa093a100dULL || + program.sync[6].words[1] != 0x00000001014d0003ULL || + program.sync[8].words[0] != 0x0000000000010802ULL || + program.sync[8].words[1] != 0x000000000001014dULL) { + std::cerr << "unexpected second producer barrier pair\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_barrier_program_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = BARRIER_HEADER.read_text(encoding="utf-8") + source = BARRIER_SOURCE.read_text(encoding="utf-8") + producer = PRODUCER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_barrier_program.h", cmake) + self.assertIn("ccu/tilexr_ccu_barrier_program.cpp", cmake) + self.assertIn("struct TileXRCcuBarrierSyncSpec", header) + self.assertIn("enum class TileXRCcuBarrierMode", header) + self.assertIn("LocalCke", header) + self.assertIn("LocalCkePostOnly", header) + self.assertIn("SyncXnPostOnly", header) + self.assertIn("SyncXnLoadPostOnly", header) + self.assertIn("SyncCkePostOnly", header) + self.assertIn("SyncCkeSetWait", header) + self.assertIn("struct TileXRCcuBarrierProgramReport", header) + self.assertIn("TileXRCcuBuildBarrierProgram", header) + self.assertIn("TileXRCcuBarrierMode::LocalCke", source) + self.assertIn("TileXRCcuBarrierMode::LocalCkePostOnly", source) + self.assertIn("TileXRCcuBarrierMode::SyncXnPostOnly", source) + self.assertIn("TileXRCcuBarrierMode::SyncXnLoadPostOnly", source) + self.assertIn("TileXRCcuBarrierMode::SyncCkePostOnly", source) + self.assertIn("TileXRCcuBarrierMode::SyncCkeSetWait", source) + self.assertIn("TileXRCcuEncodeLoadImdToXn", source) + self.assertIn("TileXRCcuEncodeSyncXn", source) + self.assertIn("TileXRCcuEncodeSyncCke", source) + self.assertIn("TileXRCcuEncodeSetCke", source) + self.assertIn("TileXRCcuEncodeClearCke", source) + self.assertIn("TileXRCcuBuildBarrierProgram", producer) + + combined = header + "\n" + source + "\n" + producer + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_basic_info_probe.py b/tests/ccu/test_tilexr_ccu_basic_info_probe.py new file mode 100644 index 0000000..654efc7 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_basic_info_probe.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import os +import shutil +import subprocess +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROBE_SOURCE = REPO_ROOT / "tests" / "ccu" / "ccu_tilexr_basic_info_probe.cpp" +COMM_DIR = REPO_ROOT / "src" / "comm" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +CCU_DRIVER_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_driver_adapter.cpp" +CCU_DIRECT_RUNTIME_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_direct_runtime.cpp" +CCU_HCCP_LOADER_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_hccp_loader.cpp" +CCU_RA_PROVIDER_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp" +CCU_SPECS_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_specs.cpp" + + +class TileXRCcuBasicInfoProbeTest(unittest.TestCase): + def compile_probe(self): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + temp_dir = tempfile.TemporaryDirectory() + temp_path = Path(temp_dir.name) + probe_bin = temp_path / "ccu_tilexr_basic_info_probe" + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(PROBE_SOURCE), + str(CCU_DIRECT_RUNTIME_SOURCE), + str(CCU_DRIVER_SOURCE), + str(CCU_HCCP_LOADER_SOURCE), + str(CCU_RA_PROVIDER_SOURCE), + str(CCU_SPECS_SOURCE), + "-ldl", + "-pthread", + "-o", + str(probe_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return temp_dir, probe_bin + + def test_probe_compiles_against_tilexr_owned_ccu_ra_chain(self): + temp_dir, _ = self.compile_probe() + temp_dir.cleanup() + + def test_probe_uses_tilexr_ra_provider_not_hcomm_runtime(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TileXRCcuDirectRuntime", + "TileXRCcuDirectRuntimeOptions", + "TileXRCcuDriverAdapter", + "TileXRCcuDecodeBasicInfo", + "deviceLogicId", + "runtime.QueryBasicInfo", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + for needle in [ + "#include + #include + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t die = 0; + uint32_t offset = 0; + uint32_t dataLen = 0; + uint32_t arraySize = 0; + }; + + struct FakeState { + std::vector deviceBytes; + std::vector calls; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + ObservedCall call; + call.op = in.op; + call.die = in.data.dataInfo.udieIdx; + call.offset = in.offsetStartIdx; + call.dataLen = in.data.dataInfo.dataLen; + call.arraySize = in.data.dataInfo.dataArraySize; + state->calls.push_back(call); + out->opRet = 0; + return 0; + } + + TileXRCcuLowerLayerInstallPlan MakeLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 7; + pfe.ctx.raw[0] = 0xa1; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb2; + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 11; + channel.ctx.raw[0] = 0xc3; + lowerLayer.channels.push_back(channel); + lowerLayer.xnClears.push_back({1, 32, 1}); + lowerLayer.ckeClears.push_back({1, 16, 1}); + return lowerLayer; + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + options.syncResourceCount = 3; + options.syncInstructionCount = 143; + options.bindingsPerSyncResource = 3; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-direct-install-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.ckeStartId = 332; + options.channelStartId = 2; + + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeLowerLayer(); + options.lowerLayerPlan = &lowerLayer; + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 1; + } + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = memoryOps; + options.repositoryMemoryUserData = &state; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + if (ret != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "direct install attempt should stop at known missing surfaces: " + << report.message << "\n"; + return 2; + } + if (!report.pipelineBuilt || !report.installAttempted || report.submitReady || + report.submitTaskCount != 0 || report.missingInstallSurfaceCount != 2 || + report.publicVerifiedInstallSurfaceCount != 4) { + std::cerr << "direct install report mismatch\n"; + return 3; + } + if (report.message.find("remote XN install provider is missing") == + std::string::npos) { + std::cerr << "direct install diagnostic is weak: " << report.message << "\n"; + return 4; + } + if (attempt.package.tasks.size() != 2 || attempt.manifest.requirements.size() != 6 || + attempt.allocation.receiptId == 0 || + attempt.repositoryReceipt.instructionStartId != attempt.package.repository.missionStartId || + attempt.repositoryReceipt.instructionCount != attempt.package.repository.missionCount || + attempt.package.tasks[0].instStartId != attempt.package.repository.missionStartId || + attempt.package.tasks[0].instCnt != 13 || + attempt.package.tasks[1].instStartId != + attempt.package.repository.missionStartId + attempt.package.repository.sqeLoadCount || + attempt.package.tasks[1].instCnt != + attempt.package.repository.missionCount - attempt.package.repository.sqeLoadCount) { + std::cerr << "attempt artifacts mismatch" + << " taskSize=" << attempt.package.tasks.size() + << " reqSize=" << attempt.manifest.requirements.size() + << " receipt=" << attempt.allocation.receiptId + << " receiptStart=" << attempt.repositoryReceipt.instructionStartId + << " missionStart=" << attempt.package.repository.missionStartId + << " receiptCount=" << attempt.repositoryReceipt.instructionCount + << " missionCount=" << attempt.package.repository.missionCount + << " task0Start=" << attempt.package.tasks[0].instStartId + << " task0Cnt=" << attempt.package.tasks[0].instCnt + << " task1Start=" << attempt.package.tasks[1].instStartId + << " expectedTask1Start=" + << attempt.package.repository.missionStartId + attempt.package.repository.sqeLoadCount + << " task1Cnt=" << attempt.package.tasks[1].instCnt + << " expectedTask1Cnt=" + << attempt.package.repository.missionCount - attempt.package.repository.sqeLoadCount + << "\n"; + return 5; + } + if (!attempt.evidence.repositoryInstalled || !attempt.evidence.missionInstalled || + !attempt.evidence.localXnInstalled || + !attempt.evidence.notifyCkeInstalled || + attempt.evidence.channelBindingsInstalled || + attempt.evidence.remoteXnBound) { + std::cerr << "attempt evidence mismatch\n"; + return 6; + } + if (state.calls.size() != 7 || + state.calls[0].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.calls[1].op != TILEXR_CCU_U_OP_SET_PFE || + state.calls[2].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.calls[3].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[4].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[5].op != TILEXR_CCU_U_OP_SET_CKE || + state.calls[6].op != TILEXR_CCU_U_OP_SET_INSTRUCTION) { + std::cerr << "unexpected direct install call sequence\n"; + return 7; + } + if (state.calls[6].offset != attempt.package.repository.missionStartId || + state.calls[6].dataLen != + attempt.package.repository.missionCount * sizeof(TileXRCcuInstr)) { + std::cerr << "repository install range mismatch\n"; + return 8; + } + if (state.calls[0].offset != 0 || state.calls[0].dataLen != 0 || + state.calls[0].arraySize != 0) { + std::cerr << "MSID token install envelope mismatch\n"; + return 10; + } + if (TileXRCcuReleaseRepositoryInstallReceipt( + attempt.repositoryReceipt, memoryOps, &state, &attempt.repositoryReleaseReport) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "repository release failed: " << attempt.repositoryReleaseReport.message << "\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_install_attempt_becomes_submit_ready_with_remote_xn_peer_exchange_proof(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t offset = 0; + uint32_t arraySize = 0; + }; + + struct FakeState { + std::vector deviceBytes; + std::vector calls; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls.push_back({in.op, in.offsetStartIdx, in.data.dataInfo.dataArraySize}); + out->opRet = 0; + return 0; + } + + TileXRCcuLowerLayerInstallPlan MakeCompleteLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa1; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb2; + lowerLayer.jettys.push_back(jetty); + + for (uint32_t i = 0; i < 3; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2 + i; + channel.ctx.raw[0] = 0xc3 + i; + lowerLayer.channels.push_back(channel); + } + + lowerLayer.xnClears.push_back({1, 1961, 14}); + lowerLayer.ckeClears.push_back({1, 332, 3}); + lowerLayer.remoteXnBindings.push_back({1, 2, 1961, 1975, 332, 0, true, 0, true, true, true}); + lowerLayer.remoteXnBindings.push_back({1, 3, 1962, 1976, 333, 0, true, 0, true, true, true}); + lowerLayer.remoteXnBindings.push_back({1, 4, 1963, 1977, 334, 0, true, 0, true, true, true}); + return lowerLayer; + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + options.syncResourceCount = 3; + options.syncInstructionCount = 143; + options.bindingsPerSyncResource = 3; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-direct-install-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.ckeStartId = 332; + options.channelStartId = 2; + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeCompleteLowerLayer(); + options.lowerLayerPlan = &lowerLayer; + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 1; + } + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = memoryOps; + options.repositoryMemoryUserData = &state; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + if (ret != TILEXR_SUCCESS) { + std::cerr << "direct install attempt should be submit-ready: " + << report.message << "\n"; + return 2; + } + if (!report.pipelineBuilt || !report.installAttempted || !report.installSucceeded || + !report.submitReady || report.missingInstallSurfaceCount != 0 || + report.publicVerifiedInstallSurfaceCount != 6 || + report.submitTaskCount != attempt.package.tasks.size()) { + std::cerr << "submit-ready direct install report mismatch\n"; + return 3; + } + if (!attempt.evidence.repositoryInstalled || !attempt.evidence.missionInstalled || + !attempt.evidence.localXnInstalled || !attempt.evidence.remoteXnBound || + !attempt.evidence.notifyCkeInstalled || !attempt.evidence.channelBindingsInstalled) { + std::cerr << "submit-ready evidence mismatch\n"; + return 4; + } + if (attempt.evidence.remoteXnSource.source.find("ValidateRemoteXnExchangeBindingProof") == + std::string::npos || + attempt.evidence.remoteXnSource.detail.find("syncXn") == std::string::npos) { + std::cerr << "remote XN evidence detail is weak\n"; + return 5; + } + if (state.calls.size() != 10 || + state.calls[0].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.calls[1].op != TILEXR_CCU_U_OP_SET_PFE || + state.calls[2].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.calls[3].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[4].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[5].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[6].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[6].arraySize != 8 || + state.calls[7].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[7].arraySize != 6 || + state.calls[8].op != TILEXR_CCU_U_OP_SET_CKE || + state.calls[8].arraySize != 3 || + state.calls[9].op != TILEXR_CCU_U_OP_SET_INSTRUCTION) { + std::cerr << "submit-ready call sequence mismatch\n"; + return 6; + } + if (TileXRCcuReleaseRepositoryInstallReceipt( + attempt.repositoryReceipt, memoryOps, &state, &attempt.repositoryReleaseReport) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "repository release failed: " << attempt.repositoryReleaseReport.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_submit_tasks_runs_submitter_in_order_and_stops_on_failure(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + + using namespace TileXR; + + struct SubmitState { + std::vector starts; + int failOnCall = 0; + }; + + int FakeSubmit(const TileXRCcuTask& task, void* stream, void* userData) + { + if (stream != reinterpret_cast(0x1234)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* state = static_cast(userData); + state->starts.push_back(task.instStartId); + if (state->failOnCall != 0 && + state->starts.size() == static_cast(state->failOnCall)) { + return TILEXR_ERROR_MKIRT; + } + return TILEXR_SUCCESS; + } + + TileXRCcuTask Task(uint16_t start) + { + TileXRCcuTask task; + task.dieId = 1; + task.missionId = 6; + task.instStartId = start; + task.instCnt = 13; + task.key = 0x059b0f03U; + task.argSize = TILEXR_CCU_SQE_ARGS_LEN; + return task; + } + + int main() + { + std::vector tasks; + tasks.push_back(Task(489)); + tasks.push_back(Task(502)); + + SubmitState okState; + TileXRCcuDirectSubmitReport report; + if (TileXRCcuSubmitPreparedTasks( + tasks, reinterpret_cast(0x1234), FakeSubmit, &okState, &report) != + TILEXR_SUCCESS) { + std::cerr << "submit batch failed: " << report.message << "\n"; + return 1; + } + if (!report.submitted || report.taskCount != 2 || report.submittedTaskCount != 2 || + okState.starts.size() != 2 || okState.starts[0] != 489 || okState.starts[1] != 502 || + report.message.find("submitted") == std::string::npos) { + std::cerr << "submit success report mismatch\n"; + return 2; + } + + SubmitState failState; + failState.failOnCall = 2; + if (TileXRCcuSubmitPreparedTasks( + tasks, reinterpret_cast(0x1234), FakeSubmit, &failState, &report) != + TILEXR_ERROR_MKIRT) { + std::cerr << "submit failure was not propagated\n"; + return 3; + } + if (report.submitted || report.taskCount != 2 || report.submittedTaskCount != 1 || + failState.starts.size() != 2 || + report.message.find("task=1") == std::string::npos || + report.message.find("ret=-2") == std::string::npos || + report.message.find("missionId=6") == std::string::npos || + report.message.find("instStartId=502") == std::string::npos || + report.message.find("key=0x59b0f03") == std::string::npos || + report.message.find("argSize=13") == std::string::npos) { + std::cerr << "submit failure report mismatch: " << report.message << "\n"; + return 4; + } + + if (TileXRCcuSubmitPreparedTasks( + tasks, nullptr, FakeSubmit, &okState, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null stream accepted\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_submit_default_runtime_trace_indexes_final_tasks(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + #include + #include + + #include + + using namespace TileXR; + + namespace { + int g_launchCount = 0; + } + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t*, rtStream_t) + { + ++g_launchCount; + return RT_ERROR_NONE; + } + + TileXRCcuTask Task(uint16_t start) + { + TileXRCcuTask task; + task.dieId = 1; + task.missionId = 6; + task.timeout = 68; + task.instStartId = start; + task.instCnt = 13; + task.key = 0x059b0f03U; + task.argSize = TILEXR_CCU_SQE_ARGS_LEN; + task.args[0] = 0x1000ULL + start; + return task; + } + + int main() + { + setenv("TILEXR_CCU_DIRECT_TRACE", "1", 1); + + std::vector tasks; + tasks.push_back(Task(489)); + tasks.push_back(Task(502)); + + TileXRCcuDirectSubmitReport report; + const int ret = TileXRCcuSubmitPreparedTasks( + tasks, reinterpret_cast(0x1234), nullptr, nullptr, &report); + if (ret != TILEXR_SUCCESS || !report.submitted || g_launchCount != 2) { + std::cerr << "submit mismatch ret=" << ret + << " submitted=" << report.submitted + << " launchCount=" << g_launchCount + << " message=" << report.message << "\n"; + return 1; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace finalRuntimeTask[0] dieId=1 missionId=6 timeout=68 instStartId=489", result.stderr) + self.assertIn("TileXRDirectCcuTrace finalRuntimeTask[1] dieId=1 missionId=6 timeout=68 instStartId=502", result.stderr) + self.assertNotIn("TileXRDirectCcuTrace finalRuntimeTask task=0", result.stderr) + + def test_direct_install_attempt_can_prepare_lower_layer_plan_after_allocation(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + std::vector ops; + }; + + struct CallbackState { + uint32_t callCount = 0; + uint32_t syncResourceCount = 0; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->ops.push_back(in.op); + out->opRet = 0; + return 0; + } + + int PrepareLowerLayerPlan( + const TileXRCcuResourceAllocation& allocation, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + void* userData) + { + auto* state = static_cast(userData); + ++state->callCount; + state->syncResourceCount = allocation.remoteXn.num; + if (plan == nullptr || report == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *plan = TileXRCcuLowerLayerInstallPlan{}; + plan->msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = allocation.channels.startId; + pfe.ctx.raw[0] = 0xa1; + plan->pfes.push_back(pfe); + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb2; + plan->jettys.push_back(jetty); + TileXRCcuLowerLayerPayloadReport payloadReport; + for (uint32_t i = 0; i < allocation.channels.num; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = allocation.channels.startId + i; + TileXRCcuChannelCtxV1Spec channelSpec; + channelSpec.remoteEid[0] = static_cast(0x10 + i); + channelSpec.tpn = 1; + channelSpec.sourcePfeId = allocation.channels.startId; + channelSpec.startJettyId = 1024; + channelSpec.jettyCount = 1; + channelSpec.dieId = 1; + channelSpec.memoryTokenId = 0x2345; + channelSpec.memoryTokenValue = 0x67890000U; + channelSpec.remoteCcuVa = 0x0000100054800000ULL; + if (TileXRCcuBuildChannelCtxV1(channelSpec, &channel.ctx, &payloadReport) != TILEXR_SUCCESS) { + report->message = payloadReport.message; + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + plan->channels.push_back(channel); + } + plan->xnClears.push_back({1, allocation.localXn.startId, allocation.localXn.num}); + plan->ckeClears.push_back({1, allocation.notifyCke.startId, allocation.notifyCke.num}); + for (uint32_t i = 0; i < allocation.remoteXn.num; ++i) { + plan->remoteXnBindings.push_back({ + 1, + static_cast(allocation.channels.startId + i), + static_cast(allocation.localXn.startId + i), + static_cast(allocation.remoteXn.startId + i), + static_cast(allocation.notifyCke.startId + i), + i, + true, + static_cast(allocation.localWaitCke.startId + i), + true, + true, + true}); + } + report->msidTokenCount = 1; + report->pfeCount = 1; + report->jettyCount = 1; + report->localJettyCtxCount = 1; + report->channelCount = allocation.channels.num; + report->ckeClearCount = 1; + report->message = "callback lower-layer plan prepared"; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + FakeState fake; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &fake, &driverReport) != TILEXR_SUCCESS) { + return 1; + } + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + options.syncResourceCount = 3; + options.syncInstructionCount = 143; + options.bindingsPerSyncResource = 3; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-callback-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.gsaStartId = 510; + options.remoteXnStartId = 2361; + options.remoteXnCount = 8; + options.ckeStartId = 332; + options.remoteNotifyCkeStartId = 364; + options.remoteNotifyCkeCount = 8; + options.channelStartId = 2; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = {FakeAlloc, FakeCopy, FakeFree}; + options.repositoryMemoryUserData = &fake; + + CallbackState callback; + options.prepareLowerLayerPlan = PrepareLowerLayerPlan; + options.lowerLayerPlanUserData = &callback; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + if (ret != TILEXR_SUCCESS || !report.submitReady || report.submitTaskCount != 2) { + std::cerr << "callback install should be submit-ready: " << report.message << "\n"; + return 2; + } + if (callback.callCount != 1 || callback.syncResourceCount != 3) { + std::cerr << "lower-layer callback was not called with allocation\n"; + return 3; + } + if (attempt.plan.kernelLocalGsa.startId != 510 || attempt.plan.kernelLocalGsa.num != 1 || + attempt.allocation.localGsa.startId != 510 || attempt.allocation.localGsa.num != 1) { + std::cerr << "GSA resource did not flow into direct CCU producer plan\n"; + return 13; + } + if (attempt.preparedLowerLayerPlan.remoteXnBindings.size() != 3 || + attempt.lowerLayerPlanReport.message.find("callback") == std::string::npos) { + std::cerr << "prepared lower-layer artifacts were not retained\n"; + return 4; + } + if (!attempt.evidence.remoteXnBound || !attempt.evidence.channelBindingsInstalled || + attempt.evidence.remoteXnSource.source.find("ValidateRemoteXnExchangeBindingProof") == + std::string::npos) { + std::cerr << "prepared lower-layer evidence mismatch\n"; + return 5; + } + const uint64_t expectedPackedToken = (1ULL << 52U) | (0x1234ULL << 32U) | 0x5678ULL; + if (attempt.package.plan.taskWindows[0].args.size() != TILEXR_CCU_SQE_ARGS_LEN || + attempt.package.plan.taskWindows[0].args[0] != basic.resourceAddr || + attempt.package.plan.taskWindows[0].args[1] != basic.resourceAddr || + attempt.package.plan.taskWindows[0].args[2] != expectedPackedToken || + attempt.package.plan.taskWindows[0].args[3] != 0x0000100054800000ULL || + attempt.submitTasks[0].args[0] != basic.resourceAddr || + attempt.submitTasks[0].args[1] != basic.resourceAddr || + attempt.submitTasks[0].args[2] != expectedPackedToken || + attempt.submitTasks[0].args[3] != 0x0000100054800000ULL) { + std::cerr << "hcomm-style SQE task args were not generated from lower-layer token state\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_install_attempt_passes_split_cke_ranges_to_lower_layer_callback(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + struct CallbackState { + bool sawSplitCke = false; + uint16_t localWaitStart = 0; + uint16_t remoteNotifyStart = 0; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + int PrepareSplitLowerLayerPlan( + const TileXRCcuResourceAllocation& allocation, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + void* userData) + { + auto* state = static_cast(userData); + state->localWaitStart = allocation.localWaitCke.startId; + state->remoteNotifyStart = allocation.remoteNotifyCke.startId; + state->sawSplitCke = + allocation.localWaitCke.startId == 332 && + allocation.localWaitCke.num == 3 && + allocation.remoteNotifyCke.startId == 364 && + allocation.remoteNotifyCke.num == 3 && + allocation.notifyCke.startId == 364 && + allocation.notifyCke.num == 3; + if (plan == nullptr || report == nullptr || !state->sawSplitCke) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + *plan = TileXRCcuLowerLayerInstallPlan{}; + plan->msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = allocation.channels.startId; + pfe.ctx.raw[0] = 0xa1; + plan->pfes.push_back(pfe); + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb2; + plan->jettys.push_back(jetty); + for (uint32_t i = 0; i < allocation.channels.num; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = allocation.channels.startId + i; + channel.ctx.raw[0] = 0xc3 + i; + plan->channels.push_back(channel); + } + plan->xnClears.push_back({1, allocation.localXn.startId, allocation.localXn.num}); + plan->ckeClears.push_back({1, allocation.localWaitCke.startId, allocation.localWaitCke.num}); + for (uint32_t i = 0; i < allocation.remoteXn.num; ++i) { + plan->remoteXnBindings.push_back({ + 1, + allocation.channels.startId + i, + static_cast(allocation.localXn.startId + i), + static_cast(allocation.remoteXn.startId + i), + static_cast(allocation.remoteNotifyCke.startId + i), + i, + true, + static_cast(allocation.localWaitCke.startId + i), + true, + true, + true}); + } + report->message = "split CKE lower-layer plan prepared"; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + FakeState fake; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &fake, &driverReport) != TILEXR_SUCCESS) { + return 1; + } + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.syncResourceCount = 3; + options.syncInstructionCount = 143; + options.bindingsPerSyncResource = 3; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-split-cke-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.ckeStartId = 332; + options.localWaitCkeStartId = 332; + options.remoteNotifyCkeStartId = 364; + options.channelStartId = 2; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = {FakeAlloc, FakeCopy, FakeFree}; + options.repositoryMemoryUserData = &fake; + + CallbackState callback; + options.prepareLowerLayerPlan = PrepareSplitLowerLayerPlan; + options.lowerLayerPlanUserData = &callback; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + if (ret != TILEXR_SUCCESS || !report.submitReady) { + std::cerr << "split CKE direct install should be submit-ready: " + << report.message << "\n"; + return 2; + } + if (!callback.sawSplitCke || callback.localWaitStart != 332 || + callback.remoteNotifyStart != 364) { + std::cerr << "split CKE allocation was not passed to callback\n"; + return 3; + } + if (attempt.plan.kernelLocalCke.startId != 332 || + attempt.plan.syncResources[0].localWaitCke != 332 || + attempt.plan.syncResources[0].notifyCke != 364 || + attempt.evidence.remoteXnSource.detail.find("syncXn") == std::string::npos) { + std::cerr << "split CKE plan or evidence mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_install_attempt_rewrites_sync_resources_from_peer_lower_layer_proof(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + int PreparePeerLowerLayerPlan( + const TileXRCcuResourceAllocation& allocation, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + void*) + { + if (plan == nullptr || report == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *plan = TileXRCcuLowerLayerInstallPlan{}; + plan->msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = allocation.channels.startId; + pfe.ctx.raw[0] = 0xa1; + plan->pfes.push_back(pfe); + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb2; + plan->jettys.push_back(jetty); + for (uint32_t i = 0; i < allocation.channels.num; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = allocation.channels.startId + i; + channel.ctx.raw[0] = 0xc3 + i; + plan->channels.push_back(channel); + } + plan->xnClears.push_back({1, allocation.localXn.startId, allocation.localXn.num}); + plan->ckeClears.push_back({1, allocation.localWaitCke.startId, allocation.localWaitCke.num}); + + for (uint32_t i = 0; i < allocation.remoteXn.num; ++i) { + const uint16_t peerLocalXn = static_cast(0x1a0 + i); + const uint16_t peerNotifyCke = static_cast(0x360 + i); + plan->remoteXnBindings.push_back({ + 1, + allocation.channels.startId + i, + static_cast(allocation.localXn.startId + i), + peerLocalXn, + peerNotifyCke, + i, + true, + static_cast(allocation.localWaitCke.startId + i), + true, + true, + true}); + } + report->message = "peer lower-layer plan prepared"; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + FakeState fake; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &fake, &driverReport) != TILEXR_SUCCESS) { + return 1; + } + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + options.syncResourceCount = 2; + options.syncInstructionCount = 9; + options.bindingsPerSyncResource = 1; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-peer-xn-rewrite-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 0x2a0; + options.ckeStartId = 0x220; + options.localWaitCkeStartId = 0x220; + options.remoteNotifyCkeStartId = 0x360; + options.channelStartId = 2; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = {FakeAlloc, FakeCopy, FakeFree}; + options.repositoryMemoryUserData = &fake; + options.prepareLowerLayerPlan = PreparePeerLowerLayerPlan; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + if (ret != TILEXR_SUCCESS || !report.submitReady) { + std::cerr << "peer XN rewrite should make direct install submit-ready: " + << report.message << "\n"; + return 2; + } + if (attempt.plan.syncResources.size() != 2 || + attempt.plan.syncResources[0].remoteXn != 0x1a0 || + attempt.plan.syncResources[1].remoteXn != 0x1a1 || + attempt.plan.syncResources[0].notifyCke != 0x360 || + attempt.plan.syncResources[1].notifyCke != 0x361) { + std::cerr << "producer plan was not rewritten from peer lower-layer proof\n"; + return 3; + } + if (attempt.package.plan.syncResources[0].remoteXn != 0x1a0 || + attempt.package.plan.syncResources[1].remoteXn != 0x1a1 || + attempt.package.plan.syncResources[0].notifyCke != 0x360 || + attempt.package.plan.syncResources[1].notifyCke != 0x361 || + attempt.submitTasks.size() != 2 || + attempt.submitTasks[0].instStartId != attempt.package.repository.missionStartId || + attempt.submitTasks[0].instCnt != 13 || + attempt.submitTasks[1].instStartId != + attempt.package.repository.missionStartId + attempt.package.repository.sqeLoadCount || + attempt.submitTasks[1].instCnt != + attempt.package.repository.missionCount - attempt.package.repository.sqeLoadCount) { + std::cerr << "launch package did not use peer lower-layer proof resources\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_install_attempt_release_helper_frees_repository_receipt_once(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + + using namespace TileXR; + + struct FakeState { + int freeCalls = 0; + int payload = 0; + }; + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != &state->payload) { + return -1; + } + ++state->freeCalls; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDirectInstallAttempt attempt; + attempt.repositoryMemoryOps.free = FakeFree; + attempt.repositoryMemoryUserData = &state; + attempt.repositoryReceipt.deviceInstructionPtr = &state.payload; + attempt.repositoryReceipt.deviceInstructionAddr = + reinterpret_cast(&state.payload); + attempt.repositoryReceipt.uploaded = true; + attempt.repositoryReceipt.installed = true; + + if (TileXRCcuReleaseDirectInstallAttemptResources(attempt) != TILEXR_SUCCESS) { + std::cerr << "first release failed: " << attempt.repositoryReleaseReport.message << "\n"; + return 1; + } + if (state.freeCalls != 1 || attempt.repositoryReceipt.deviceInstructionPtr != nullptr || + attempt.repositoryReleaseReport.message != "ok") { + std::cerr << "release did not clear repository receipt\n"; + return 2; + } + if (TileXRCcuReleaseDirectInstallAttemptResources(attempt) != TILEXR_SUCCESS || + state.freeCalls != 1) { + std::cerr << "release helper is not idempotent\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_install_attempt_trace_dumps_final_peer_resources_and_tasks_when_enabled(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + #include + + using namespace TileXR; + + int main() + { + setenv("TILEXR_CCU_DIRECT_TRACE", "1", 1); + + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.syncResourceCount = 1; + options.syncInstructionCount = 2; + options.bindingsPerSyncResource = 1; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-direct-trace-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.ckeStartId = 332; + options.channelStartId = 2; + options.offlineOnly = true; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + (void)ret; + if (!report.pipelineBuilt || attempt.package.tasks.empty()) { + std::cerr << "trace setup failed before package build: " << report.message << "\n"; + return 1; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace", result.stderr) + self.assertIn("syncResource[0]", result.stderr) + self.assertIn("remoteXn=", result.stderr) + self.assertIn("taskWindow[0]", result.stderr) + self.assertIn("task[0]", result.stderr) + self.assertNotIn("program.sqeLoad[0]", result.stderr) + self.assertIn("program.sync[0]", result.stderr) + + def test_direct_install_attempt_trace_decodes_lower_layer_contexts_when_enabled(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int PrepareLowerLayer( + const TileXRCcuResourceAllocation& allocation, + TileXRCcuLowerLayerInstallPlan* plan, + TileXRCcuLowerLayerPlanBuilderReport* report, + void* userData) + { + auto* basic = static_cast(userData); + + std::array eid; + for (uint32_t i = 0; i < eid.size(); ++i) { + eid[i] = static_cast(0x20 + i); + } + + TileXRCcuLowerLayerPlanSpec spec; + spec.msidToken.dieId = basic->dieId; + spec.msidToken.msId = basic->msId; + spec.msidToken.tokenId = basic->msidToken.tokenId; + spec.msidToken.tokenValue = basic->msidToken.tokenValue; + spec.msidToken.valid = basic->msidToken.valid; + spec.pfe.dieId = basic->dieId; + spec.pfe.pfeOffset = allocation.channels.startId; + spec.pfe.startJettyId = 0x400; + spec.pfe.startLocalJettyCtxId = 0x02; + + TileXRCcuLowerLayerJettySpec jetty; + jetty.dieId = basic->dieId; + jetty.pfeId = allocation.channels.startId; + jetty.startJettyCtxId = spec.pfe.startLocalJettyCtxId; + jetty.doorbellVa = 0x1122334455667788ULL; + jetty.doorbellTokenId = 0xabcdeU; + jetty.doorbellTokenValue = 0x12345678U; + jetty.sqDepth = 8; + jetty.wqeBasicBlockStartId = 0x34; + spec.jettys.push_back(jetty); + + TileXRCcuLowerLayerChannelSpec channel; + channel.dieId = basic->dieId; + channel.channelId = allocation.channels.startId; + channel.remoteEid = eid; + channel.tpn = 0x00876543U; + channel.sourcePfeId = allocation.channels.startId; + channel.startJettyId = 0x400; + channel.jettyCount = 1; + channel.memoryTokenId = 0x23456U; + channel.memoryTokenValue = 0x3456789aU; + channel.remoteCcuVa = + basic->resourceAddr + TILEXR_CCU_V1_XN_RESOURCE_OFFSET + allocation.remoteXn.startId * 8ULL; + spec.channels.push_back(channel); + + spec.xnClear.dieId = basic->dieId; + spec.xnClear.startXnId = allocation.localXn.startId; + spec.xnClear.count = allocation.localXn.num; + spec.xnClear.valid = true; + spec.ckeClear.dieId = basic->dieId; + spec.ckeClear.startCkeId = allocation.notifyCke.startId; + spec.ckeClear.count = allocation.notifyCke.num; + spec.ckeClear.valid = true; + + TileXRCcuRemoteXnBindingProof proof; + proof.dieId = basic->dieId; + proof.channelId = allocation.channels.startId; + proof.localXn = allocation.localXn.startId; + proof.remoteXn = allocation.remoteXn.startId; + proof.notifyCke = allocation.notifyCke.startId; + proof.localWaitCke = allocation.notifyCke.startId; + proof.peerRank = 0; + proof.peerExchangeObserved = true; + proof.endpointRouteVerified = true; + spec.remoteXnBindings.push_back(proof); + + return TileXRCcuBuildLowerLayerInstallPlan(spec, plan, report); + } + + int main() + { + setenv("TILEXR_CCU_DIRECT_TRACE", "1", 1); + + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.valid = true; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0x5678; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.syncResourceCount = 1; + options.syncInstructionCount = 2; + options.bindingsPerSyncResource = 1; + options.deviceId = 3; + options.rank = 1; + options.provider = "unit-test-direct-trace-lower-layer-provider"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.xnStartId = 1961; + options.ckeStartId = 332; + options.channelStartId = 2; + options.offlineOnly = true; + options.prepareLowerLayerPlan = PrepareLowerLayer; + options.lowerLayerPlanUserData = &basic; + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectInstallAttempt(options, &attempt, &report); + (void)ret; + if (!report.pipelineBuilt || attempt.preparedLowerLayerPlan.channels.empty()) { + std::cerr << "lower-layer trace setup failed: " << report.message << "\n"; + return 1; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("decoded=PfeCtx", result.stderr) + self.assertIn("decoded=LocalJettyCtx", result.stderr) + self.assertIn("decoded=ChannelCtxV1", result.stderr) + self.assertIn("startTaJettyId=1024", result.stderr) + self.assertIn("startLocalJettyCtxId=2", result.stderr) + self.assertIn("doorbellVa=0x1122334455667788", result.stderr) + self.assertIn("wqeBasicBlockStartId=52", result.stderr) + self.assertIn("sourcePfeId=2", result.stderr) + self.assertIn("remoteCcuVa=", result.stderr) + + def test_direct_orchestrator_is_wired_and_has_no_private_runtime_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = DIRECT_HEADER.read_text(encoding="utf-8") + source = DIRECT_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_direct_orchestrator.h", cmake) + self.assertIn("ccu/tilexr_ccu_direct_orchestrator.cpp", cmake) + self.assertIn("struct TileXRCcuDirectInstallOptions", header) + self.assertIn("struct TileXRCcuDirectMemoryCopySpec", header) + self.assertIn("struct TileXRCcuDirectInstallAttempt", header) + self.assertIn("struct TileXRCcuDirectInstallReport", header) + self.assertIn("struct TileXRCcuDirectSubmitReport", header) + self.assertIn("TileXRCcuLowerLayerPlanPrepareFn", header) + self.assertIn("localWaitCkeStartId", header) + self.assertIn("gsaStartId", header) + self.assertIn("remoteNotifyCkeStartId", header) + self.assertIn("prepareLowerLayerPlan", header) + self.assertIn("preparedLowerLayerPlan", header) + self.assertIn("lowerLayerPlanReport", header) + self.assertIn("TileXRCcuSubmitPreparedTasks", header) + self.assertIn("TileXRCcuRunDirectInstallAttempt", header) + self.assertIn("TileXRCcuRunDirectMemoryCopyInstallAttempt", header) + self.assertIn("TileXRCcuDecodeBasicInfo", source) + self.assertIn("TileXRCcuBuildResourceSpec", source) + self.assertIn("TileXRCcuResourceAllocator", source) + self.assertIn("TileXRCcuBuildLaunchPackage", source) + self.assertIn("TileXRCcuBuildMemoryCopyProgram", source) + self.assertIn("BuildDirectMemoryCopyLaunchPackage", source) + self.assertIn("TileXRCcuBindLaunchPackageInstallScope", source) + self.assertIn("TileXRCcuBuildInstallManifest", source) + self.assertIn("options.prepareLowerLayerPlan", source) + self.assertIn("TileXRCcuInstallHardware", source) + self.assertIn("TileXRCcuPrepareSubmitTasks", source) + self.assertIn("TileXRCcuSubmitTask", source) + self.assertIn("remote XN install provider is missing", source) + self.assertIn("TraceDecodedInstr", source) + self.assertIn("TraceDecodedPfeCtx", source) + self.assertIn("TraceDecodedLocalJettyCtx", source) + self.assertIn("TraceDecodedChannelCtxV1", source) + self.assertIn("decoded=SyncXn", source) + self.assertIn("decoded=SyncCke", source) + self.assertIn("decoded=LoadImdToXn", source) + self.assertIn("decoded=LoadImdToGSA", source) + self.assertIn("decoded=TransRmtMemToLocMem", source) + self.assertIn("decoded=TransLocMemToRmtMem", source) + self.assertIn("TILEXR_CCU_TRACE_TRANS_RMT_MEM_TO_LOC_MEM_HEADER", source) + self.assertIn("TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_RMT_MEM_HEADER", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_direct_install_options_default_to_lower_layer_first(self): + header = DIRECT_HEADER.read_text(encoding="utf-8") + source = DIRECT_SOURCE.read_text(encoding="utf-8") + + self.assertIn( + "TileXRCcuInstallOrder installOrder = " + "TileXRCcuInstallOrder::InstallLowerLayerFirst", + header, + ) + self.assertIn("installRequest.installOrder = options.installOrder", source) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_direct_smoke_probe.py b/tests/ccu/test_tilexr_ccu_direct_smoke_probe.py new file mode 100644 index 0000000..29f9f6f --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_direct_smoke_probe.py @@ -0,0 +1,596 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import os +import platform +import shutil +import subprocess +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROBE_SOURCE = REPO_ROOT / "tests" / "ccu" / "ccu_tilexr_direct_smoke_probe.cpp" +COMM_DIR = REPO_ROOT / "src" / "comm" +INCLUDE_DIR = REPO_ROOT / "src" / "include" + + +PRIVATE_CCU_NEEDLES = [ + "#include ", + "#include \"hccl.h\"", + "pkg_inc/hcomm", + "pkg_inc/hccl", + "include/hccl", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "HcclChannelAcquire", + "HcclGetChannelForCcu", + "CcuResBatchAllocator", + "CcuResRepository", + "CcuDeviceManager", + "CcuKernelMgr", + "CtxMgrImp", + "GeneTaskParam", + "GetMissionKey", + "SetMissionId", + "SetMissionKey", + "SetInstrId", + "SetCcuInstrInfo", + "LoadInstruction", + "AllocIns", + "AllocCke", + "AllocXn", +] + + +def cann_paths(): + ascend_home = os.environ.get("ASCEND_HOME_PATH") or os.environ.get("ASCEND_HOME") + if not ascend_home: + return None + + arch = os.environ.get("ARCH") + if not arch: + arch = "aarch64" if platform.machine() in ("aarch64", "arm64") else "x86_64" + + cann_root = Path(ascend_home) / f"{arch}-linux" + include_dirs = [ + cann_root / "pkg_inc", + cann_root / "pkg_inc" / "runtime", + cann_root / "include", + ] + if not any((include_dir / "acl" / "acl.h").exists() for include_dir in include_dirs): + return None + + lib_dir = cann_root / "lib64" + if not (lib_dir / "libascendcl.so").exists(): + return None + driver_root = Path(os.environ.get("ASCEND_DRIVER_PATH", "/usr/local/Ascend/driver")) + driver_lib_dir = driver_root / "lib64" / "driver" + return include_dirs, lib_dir, driver_lib_dir + + +def find_tile_comm(): + env_path = os.environ.get("TILEXR_TILE_COMM_LIB") + candidates = [] + if env_path: + candidates.append(Path(env_path)) + candidates.extend( + [ + REPO_ROOT / "build" / "src" / "comm" / "libtile-comm.so", + REPO_ROOT / "install" / "lib64" / "libtile-comm.so", + REPO_ROOT / "install" / "lib" / "libtile-comm.so", + REPO_ROOT / "install_direct_ccu_guard" / "lib64" / "libtile-comm.so", + ] + ) + for candidate in candidates: + if candidate.exists(): + return candidate + return None + + +class TileXRCcuDirectSmokeProbeTest(unittest.TestCase): + def compile_probe(self): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + cann_config = cann_paths() + if cann_config is None: + self.skipTest("CANN ACL headers/libs are not configured for direct CCU smoke probe") + tile_comm = find_tile_comm() + if tile_comm is None: + self.skipTest("libtile-comm.so is not built; build tile-comm before compiling direct smoke probe") + + include_dirs, cann_lib_dir, driver_lib_dir = cann_config + temp_dir = tempfile.TemporaryDirectory() + temp_path = Path(temp_dir.name) + probe_bin = temp_path / "ccu_tilexr_direct_smoke_probe" + compile_cmd = [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + ] + for include_dir in include_dirs: + if include_dir.exists(): + compile_cmd.extend(["-I", str(include_dir)]) + compile_cmd.extend( + [ + str(PROBE_SOURCE), + "-L", + str(tile_comm.parent), + "-L", + str(cann_lib_dir), + "-L", + str(driver_lib_dir), + f"-Wl,-rpath-link,{tile_comm.parent}", + f"-Wl,-rpath-link,{cann_lib_dir}", + f"-Wl,-rpath-link,{driver_lib_dir}", + "-ltile-comm", + "-lascendcl", + "-lruntime", + "-ldl", + "-pthread", + "-o", + str(probe_bin), + ] + ) + subprocess.run( + compile_cmd, + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return temp_dir, probe_bin, tile_comm.parent, cann_lib_dir, driver_lib_dir + + def test_source_exists_and_defines_default_safe_env_guards(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_ENABLE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_TRACE_LIFECYCLE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SUBMIT", source) + self.assertIn("TILEXR_CCU_PROBE_RANK", source) + self.assertIn("TILEXR_CCU_PROBE_RANK_SIZE", source) + self.assertIn("TILEXR_CCU_PROBE_DEVICE", source) + self.assertIn("TILEXR_CCU_PROBE_COMM_DOMAIN", source) + self.assertIn("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START", source) + self.assertIn("TILEXR_CCU_PROBE_SQE_ARG_COUNT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES", source) + self.assertIn("TILEXR_CCU_DIRECT_BARRIER_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE", source) + self.assertIn("TILEXR_DIRECT_CCU_REPOSITORY_MEMORY_ALLOC_RT_HBM", source) + self.assertIn('text == "rt_hbm"', source) + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_ORDER", source) + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID", source) + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID", source) + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE", source) + self.assertIn("InitCommForDirectCcuSmoke", source) + self.assertIn("RunThreadModeSmoke", source) + self.assertIn("ShouldFastExitAfterPrepareFailure", source) + self.assertIn("ShouldFastExitAfterRun", source) + self.assertIn("TraceLifecycle", source) + self.assertIn("RepositoryInstallWindowFromEnv", source) + self.assertIn("RepositoryInstallDataLenModeFromEnv", source) + self.assertIn("RepositoryMemoryAllocModeFromEnv", source) + self.assertIn("InstallOrderFromEnv", source) + self.assertIn("TileXRCommInitAll", source) + self.assertIn("DefaultSyncInstructionCount", source) + self.assertIn('std::string(value) == "sync_cke"', source) + self.assertIn('std::string(value) == "sync_cke_set_wait"', source) + self.assertIn('std::string(value) == "sync_cke_post_only"', source) + self.assertIn('std::string(value) == "local_cke"', source) + self.assertIn('std::string(value) == "local_cke_post_only"', source) + self.assertIn('std::string(value) == "sync_xn_post_only"', source) + self.assertIn('std::string(value) == "sync_xn_load_post_only"', source) + self.assertIn("TileXRCommInitRankWithDomain", source) + self.assertIn("TileXRCommInitRankDirectCcuWithDomain", source) + self.assertIn("TileXRCommPrepareDirectCcu", source) + self.assertIn("TileXRCommPrepareDirectCcuMemoryCopy", source) + self.assertIn("TileXRDirectCcuSubmitPrepared", source) + self.assertIn("TileXRDirectCcuDestroyPrepared", source) + self.assertIn("tilexr_ccu_direct_smoke config", source) + self.assertIn("barrierMode=", source) + self.assertIn("repositoryInstallWindow=", source) + self.assertIn("repositoryInstallDataLenMode=", source) + self.assertIn("repositoryMemoryAllocMode=", source) + self.assertIn("installOrder=", source) + self.assertIn("resourceWindowTokenId=", source) + self.assertIn("resourceWindowRawTokenId=", source) + self.assertIn("resourceWindowTokenValue=", source) + self.assertIn("tilexr_ccu_direct_smoke preparedTasks", source) + self.assertIn("tilexr_ccu_direct_smoke submitTiming", source) + self.assertIn("tilexr_ccu_direct_smoke p2pCcuCopy", source) + self.assertIn("aclrtMemcpy", source) + self.assertIn("ACL_MEMCPY_HOST_TO_DEVICE", source) + self.assertIn("ACL_MEMCPY_DEVICE_TO_HOST", source) + self.assertIn("passed=", source) + self.assertIn("syncMs=", source) + self.assertIn("std::this_thread::sleep_for", source) + self.assertIn("std::vector", source) + self.assertIn("std::vector", source) + + def test_p2p_ccu_copy_mode_prepares_memory_copy_task(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("kP2pCcuCopyEnv", source) + self.assertIn("RunP2pCcuCopy", source) + self.assertIn("TileXRDirectCcuMemoryCopyPrepareOptions", source) + self.assertIn("TileXRCommPrepareDirectCcuMemoryCopy", source) + self.assertIn("ACL_MEMCPY_HOST_TO_DEVICE", source) + self.assertIn("ACL_MEMCPY_DEVICE_TO_HOST", source) + self.assertIn("p2pCcuCopy", source) + + def test_thread_mode_path_uses_single_process_init_and_never_rank_ipc_init(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + thread_mode_pos = source.index("RunThreadModeSmoke") + thread_mode_body = source[thread_mode_pos: source.index("int main()")] + prepared_body = source[source.index("RunPreparedSmokeForRank"): thread_mode_pos] + self.assertIn("TileXRCommInitAll", thread_mode_body) + self.assertIn("RunPreparedSmokeForRank", thread_mode_body) + self.assertIn("TileXRCommPrepareDirectCcu", prepared_body) + self.assertIn("TileXRDirectCcuSubmitPrepared", prepared_body) + self.assertNotIn("TileXRCommInitRankWithDomain", thread_mode_body) + self.assertNotIn("TILEXR_COMM_ID", thread_mode_body) + + def test_thread_mode_worker_sets_device_before_direct_ccu_submit(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + thread_mode_body = source[source.index("int RunThreadModeSmoke"): source.index("int main()")] + + worker_body = thread_mode_body[ + thread_mode_body.index("rankThreads.emplace_back"): + thread_mode_body.index("for (auto& rankThread", thread_mode_body.index("rankThreads.emplace_back")) + ] + self.assertIn("aclrtSetDevice(devices[rank])", worker_body) + self.assertLess( + worker_body.index("aclrtSetDevice(devices[rank])"), + worker_body.index("RunPreparedSmokeForRank"), + ) + + def test_process_mode_can_opt_into_direct_ccu_only_init_to_bypass_peer_ipc(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + init_helper_body = source[ + source.index("int InitCommForDirectCcuSmoke"): + source.index("TileXRDirectCcuPrepareOptions MakePrepareOptions") + ] + main_source = source[source.index("int main()"):] + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT", source) + self.assertIn("TileXRCommInitRankDirectCcuWithDomain", init_helper_body) + self.assertIn("TileXRCommInitRankWithDomain", init_helper_body) + self.assertIn("EnvFlag(kDirectCcuOnlyInitEnv)", init_helper_body) + self.assertIn("InitCommForDirectCcuSmoke", main_source) + + def test_process_mode_fast_exit_skips_comm_destroy_after_prepare_failure(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + main_source = source[source.index("int main()"):] + + final_ret_pos = main_source.index("int finalRet = RunPreparedSmokeForRank") + fast_exit_pos = main_source.index("ShouldFastExitAfterPrepareFailure(finalRet)") + destroy_pos = main_source.index("TileXRCommDestroy(raw)", final_ret_pos) + reset_pos = main_source.index("aclrtResetDevice(device)", final_ret_pos) + finalize_pos = main_source.index("aclFinalize()", final_ret_pos) + + self.assertLess(final_ret_pos, fast_exit_pos) + self.assertLess(fast_exit_pos, destroy_pos) + self.assertLess(fast_exit_pos, reset_pos) + self.assertLess(fast_exit_pos, finalize_pos) + self.assertIn("std::fflush(stdout)", main_source) + self.assertIn("std::fflush(stderr)", main_source) + self.assertIn("std::_Exit(finalRet)", main_source) + self.assertIn("tilexr_ccu_direct_smoke fastExitOnPrepareFailure=1", source) + + def test_fast_exit_reason_distinguishes_runtime_failures_from_prepare_failure(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + main_source = source[source.index("int main()"):] + + self.assertIn("FastExitReasonForReturnCode", source) + self.assertIn("direct CCU collective completion timed out; skipping cleanup", source) + self.assertIn("direct CCU P2P CCU-copy check failed; skipping cleanup", source) + self.assertIn("FastExitReasonForReturnCode(finalRet)", main_source) + self.assertNotIn( + 'reason="prepare failed; skipping cleanup to preserve diagnostic status"', + main_source, + ) + + def test_process_mode_can_fast_exit_after_run_to_isolate_cleanup_hangs(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + main_source = source[source.index("int main()"):] + + final_ret_pos = main_source.index("int finalRet = RunPreparedSmokeForRank") + fast_exit_pos = main_source.index("ShouldFastExitAfterRun()") + destroy_pos = main_source.index("TileXRCommDestroy(raw)", final_ret_pos) + + self.assertLess(final_ret_pos, fast_exit_pos) + self.assertLess(fast_exit_pos, destroy_pos) + self.assertIn("tilexr_ccu_direct_smoke fastExitAfterRun=1", source) + self.assertIn("TraceLifecycle(\"before TileXRDirectCcuDestroyPrepared\")", source) + self.assertIn("TraceLifecycle(\"after TileXRDirectCcuDestroyPrepared\")", source) + self.assertIn("TraceLifecycle(\"before aclrtSynchronizeStream\")", source) + self.assertIn("TraceLifecycle(\"after aclrtSynchronizeStream\")", source) + + def test_submit_task_selector_can_isolate_prepared_task_hangs(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepared_body = source[ + source.index("int RunPreparedSmokeForRank"): + source.index("int RunThreadModeSmoke") + ] + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SUBMIT_TASK_SELECTOR", source) + self.assertIn("SubmitPreparedWithSelector", source) + self.assertIn("TileXRDirectCcuGetPreparedTask", source) + self.assertIn("TileXRDirectCcuSubmitPreparedTask", source) + self.assertIn('selector == "first"', source) + self.assertIn('selector == "second"', source) + self.assertIn("submitTaskSelector=", source) + self.assertIn("TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport)", prepared_body) + self.assertIn("SubmitPreparedWithSelector(prepared, installReport.submitTaskCount, stream", prepared_body) + + def test_process_mode_submit_uses_collective_ready_gate(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + runner = (REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_smoke.sh").read_text(encoding="utf-8") + prepared_body = source[ + source.index("int RunPreparedSmokeForRank"): + source.index("int RunThreadModeSmoke") + ] + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_READY_DIR", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_READY_TIMEOUT_MS", source) + self.assertIn("WaitForCollectiveSubmitReadiness", source) + self.assertIn("WriteSubmitReadiness", source) + self.assertIn("collectiveSubmitReady", prepared_body) + self.assertIn("collective submitReady gate did not pass", source) + self.assertLess( + prepared_body.index("WaitForCollectiveSubmitReadiness"), + prepared_body.index("aclrtCreateStream"), + ) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_READY_DIR=${ready_dir}", runner) + self.assertIn("rm -rf \"${ready_dir}\"", runner) + + def test_process_mode_submit_waits_for_all_ranks_before_cleanup(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + runner = (REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_smoke.sh").read_text(encoding="utf-8") + prepared_body = source[ + source.index("int RunPreparedSmokeForRank"): + source.index("int RunThreadModeSmoke") + ] + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DONE_DIR", source) + self.assertIn("WaitForCollectiveSubmitDone", source) + self.assertIn("WaitForCollectiveSubmitDone(rank, rankSize, finalRet)", prepared_body) + self.assertIn("collectiveSubmitDone", source) + self.assertLess( + prepared_body.index("aclrtSynchronizeStream"), + prepared_body.index("WaitForCollectiveSubmitDone"), + ) + self.assertLess( + prepared_body.index("WaitForCollectiveSubmitDone"), + prepared_body.index("TileXRDirectCcuDestroyPrepared"), + ) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DONE_DIR=${done_dir}", runner) + self.assertIn("rm -rf \"${ready_dir}\" \"${done_dir}\"", runner) + + def test_probe_wires_gsa_and_split_cke_env_into_prepare_options_and_config_trace(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TILEXR_CCU_PROBE_GSA_START", + "TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START", + "TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT", + "TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START", + "TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT", + "options.localWaitCkeStartId", + "options.localWaitCkeCount", + "options.remoteNotifyCkeStartId", + "options.remoteNotifyCkeCount", + "options.repositoryInstallWindow", + "options.repositoryInstallDataLenMode", + "options.repositoryMemoryAllocMode", + "options.installOrder", + "options.sqeArgCount", + "options.missionInstructionStartId", + "options.gsaStartId", + "sqeArgCount=", + "gsaStartId=", + "localWaitCkeStartId=", + "localWaitCkeCount=", + "remoteNotifyCkeStartId=", + "remoteNotifyCkeCount=", + "repositoryInstallWindow=", + "repositoryInstallDataLenMode=", + "repositoryMemoryAllocMode=", + "installOrder=", + "missionInstructionStartId=", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_probe_prepared_task_trace_prints_full_runtime_args(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepared_body = source[ + source.index("void PrintPreparedTasks"): + source.index("int RunPreparedSmokeForRank") + ] + + self.assertIn("TILEXR_DIRECT_CCU_SQE_ARGS_LEN", prepared_body) + self.assertIn("arg < TILEXR_DIRECT_CCU_SQE_ARGS_LEN", prepared_body) + self.assertIn('<< ".arg" << arg << "=0x"', prepared_body) + self.assertIn("task.args[arg]", prepared_body) + self.assertNotIn(".arg0=0x", prepared_body) + + def test_probe_defaults_to_lower_layer_first_install_order(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + install_order_body = source[ + source.index("uint32_t InstallOrderFromEnv()"): + source.index("uint32_t DefaultSyncInstructionCount") + ] + + self.assertIn("TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST", install_order_body) + self.assertIn("repository_first", install_order_body) + self.assertIn("TILEXR_DIRECT_CCU_INSTALL_ORDER_REPOSITORY_FIRST", install_order_body) + self.assertLess( + install_order_body.index("TILEXR_DIRECT_CCU_INSTALL_ORDER_LOWER_LAYER_FIRST"), + install_order_body.index("TILEXR_DIRECT_CCU_INSTALL_ORDER_REPOSITORY_FIRST"), + ) + + def test_probe_default_sync_instruction_count_includes_hcomm_style_task1_prelude(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + default_body = source[ + source.index("uint32_t DefaultSyncInstructionCount"): + source.index("const char* FirstEnv") + ] + + self.assertIn("kHcommStyleTask1PreludeInstructionCount", source) + self.assertIn( + "return kHcommStyleTask1PreludeInstructionCount + syncResourceCount;", + default_body, + ) + self.assertIn( + "return kHcommStyleTask1PreludeInstructionCount + syncResourceCount * 2U;", + default_body, + ) + + def test_thread_mode_rank_specific_resource_env_overrides_common_prepare_options(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepare_options_body = source[ + source.index("TileXRDirectCcuPrepareOptions MakePrepareOptions"): + source.index("void PrintInstallReport") + ] + + self.assertIn("RankEnvInt", prepare_options_body) + for suffix in [ + "XN_START", + "LOCAL_WAIT_CKE_START", + "LOCAL_WAIT_CKE_COUNT", + "REMOTE_NOTIFY_CKE_START", + "REMOTE_NOTIFY_CKE_COUNT", + ]: + with self.subTest(suffix=suffix): + self.assertIn(f"TILEXR_CCU_PROBE_RANK\", rank, \"_{suffix}", source) + self.assertIn( + 'RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_XN_START", "TILEXR_CCU_PROBE_XN_START", 1)', + prepare_options_body, + ) + self.assertIn( + 'RankEnvInt("TILEXR_CCU_PROBE_RANK", rank, "_LOCAL_WAIT_CKE_START", kLocalWaitCkeStartEnv, 0)', + prepare_options_body, + ) + + def test_default_skip_happens_before_acl_comm_prepare_or_submit(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + main_source = source[source.index("int main()"):] + + guard_pos = main_source.index("if (!EnvFlag(kEnableEnv))") + for needle in [ + "aclInit(", + "InitCommForDirectCcuSmoke", + "RunPreparedSmokeForRank", + ]: + with self.subTest(needle=needle): + self.assertLess(guard_pos, main_source.index(needle)) + + def test_probe_keeps_hcomm_hccl_and_runtime_launch_out_of_source(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + for needle in PRIVATE_CCU_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, source) + self.assertNotIn("runtime/kernel.h", source) + self.assertNotIn("rtCCULaunch", source) + + def test_probe_compiles_and_default_run_skips_without_touching_hardware(self): + temp_dir, probe_bin, tile_comm_dir, cann_lib_dir, driver_lib_dir = self.compile_probe() + try: + env = os.environ.copy() + env.pop("TILEXR_CCU_DIRECT_SMOKE_ENABLE", None) + env["LD_LIBRARY_PATH"] = ( + str(tile_comm_dir) + + os.pathsep + + str(cann_lib_dir) + + os.pathsep + + str(driver_lib_dir) + + os.pathsep + + env.get("LD_LIBRARY_PATH", "") + ) + result = subprocess.run( + [str(probe_bin)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + finally: + temp_dir.cleanup() + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_smoke skipped", result.stdout) + self.assertNotIn("prepare ret=", result.stdout) + self.assertNotIn("submit ret=", result.stdout) + + def test_optional_probe_runtime_when_enabled(self): + if os.environ.get("TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE") != "1": + self.skipTest("set TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 to run direct CCU smoke on hardware") + rank_size = ( + os.environ.get("TILEXR_CCU_PROBE_RANK_SIZE") + or os.environ.get("PMI_SIZE") + or os.environ.get("OMPI_COMM_WORLD_SIZE") + or os.environ.get("MV2_COMM_WORLD_SIZE") + or os.environ.get("RANK_SIZE") + ) + if rank_size is None or int(rank_size) <= 1: + self.skipTest("direct CCU prepare smoke requires a real multi-rank TileXRComm launch") + temp_dir, probe_bin, tile_comm_dir, cann_lib_dir, driver_lib_dir = self.compile_probe() + try: + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_SMOKE_ENABLE"] = "1" + env["LD_LIBRARY_PATH"] = ( + str(tile_comm_dir) + + os.pathsep + + str(cann_lib_dir) + + os.pathsep + + str(driver_lib_dir) + + os.pathsep + + env.get("LD_LIBRARY_PATH", "") + ) + result = subprocess.run( + [str(probe_bin)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + finally: + temp_dir.cleanup() + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_smoke prepare ret=0", result.stdout) + self.assertIn("submitReady=", result.stdout) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_direct_smoke_runner.py b/tests/ccu/test_tilexr_ccu_direct_smoke_runner.py new file mode 100644 index 0000000..94c31ca --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_direct_smoke_runner.py @@ -0,0 +1,1399 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import os +import subprocess +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +RUNNER = REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_smoke.sh" +EVENING_RUNNER = REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_evening_smoke.sh" +BUSY_GUARD = REPO_ROOT / "tests" / "ccu" / "ccu_npu_smi_busy_guard.py" + + +class TileXRCcuDirectSmokeRunnerTest(unittest.TestCase): + def test_runner_is_default_safe_and_documents_hardware_gate(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn("TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_ENABLE=1", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=1", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SUBMIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_EXPECT_BARRIER_WAIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_MIN_SYNC_MS", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES", source) + self.assertIn("TILEXR_CCU_DIRECT_BARRIER_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_ORDER", source) + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_DIE_ID", source) + self.assertIn("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START", source) + self.assertIn("TILEXR_CCU_PROBE_SQE_ARG_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_MISSION_START", source) + self.assertIn("TILEXR_CCU_PROBE_INSTRUCTION_START", source) + self.assertIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_BINDINGS_PER_RESOURCE", source) + self.assertIn("TILEXR_CCU_PROBE_CHANNEL_START", source) + self.assertIn("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START", source) + self.assertIn("TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START", source) + self.assertIn("TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_RANK0_XN_START", source) + self.assertIn("TILEXR_CCU_PROBE_RANK1_XN_START", source) + self.assertIn("TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START", source) + self.assertIn("TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START", source) + for suffix in [ + "EID", + "TPN", + "DOORBELL_VA", + "DOORBELL_TOKEN_ID", + "DOORBELL_TOKEN_VALUE", + "SQ_DEPTH", + ]: + with self.subTest(endpoint_suffix=suffix): + self.assertIn(f" {suffix}", source) + self.assertIn('endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}"', source) + self.assertIn('rank0_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK0"', source) + self.assertIn('rank1_endpoint_var="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}_RANK1"', source) + self.assertIn('rank0_env+=("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank0_endpoint_value}")', source) + self.assertIn('rank1_env+=("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}=${rank1_endpoint_value}")', source) + self.assertIn("rank0_env", source) + self.assertIn("rank1_env", source) + self.assertIn("TILEXR_COMM_ID", source) + self.assertIn("TILEXR_CCU_PROBE_RANK_SIZE=2", source) + self.assertIn("TILEXR_CCU_PROBE_RANK=0", source) + self.assertIn("TILEXR_CCU_PROBE_RANK=1", source) + self.assertIn('if [ "${TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE:-0}" = "1" ]', source) + self.assertIn('"${probe_bin}" > "${thread_log}" 2>&1', source) + self.assertIn("ccu_thread.log", source) + self.assertIn("ccu_rank0.log", source) + self.assertIn("ccu_rank1.log", source) + self.assertIn("installSucceeded=1", source) + self.assertIn("submitReady=1", source) + self.assertLess(source.index("installSucceeded=1"), source.index("submitReady=1")) + self.assertIn("${repo_root}/install/lib64/libtile-comm.so", source) + self.assertIn( + 'timeout "${timeout_s}s" env "${common_env[@]}" "${rank0_env[@]}" TILEXR_CCU_PROBE_RANK=0', + source, + ) + self.assertIn( + 'timeout "${timeout_s}s" env "${common_env[@]}" "${rank1_env[@]}" TILEXR_CCU_PROBE_RANK=1', + source, + ) + self.assertNotIn('bash -c "wait', source) + self.assertIn("npu-smi rc=", source) + self.assertIn("TILEXR_CCU_SMOKE_ALLOW_BUSY_NPU", source) + self.assertIn("TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU", source) + self.assertIn("--allow-unhealthy", source) + self.assertIn("TILEXR_CCU_SMOKE_REQUIRE_NPU_SMI", source) + self.assertIn("ccu_npu_smi_busy_guard.py", source) + self.assertIn("tilexr_ccu_direct_smoke_runner summary", source) + self.assertIn("rank0Status=", source) + self.assertIn("rank1Status=", source) + self.assertIn("rank0Log=", source) + self.assertIn("rank1Log=", source) + self.assertIn("submitTiming", source) + self.assertIn("syncMs=", source) + self.assertIn("p2pCcuCopy", source) + self.assertIn("passed=1", source) + self.assertIn('"TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE=${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE:-1}"', source) + + gate = source.index('if [ "${TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE:-0}" != "1" ]') + for needle in [ + "npu-smi info", + '"${probe_bin}"', + "TILEXR_CCU_DIRECT_SMOKE_ENABLE=1", + "TILEXR_CCU_PROBE_RANK=0", + "TILEXR_CCU_PROBE_RANK=1", + ]: + with self.subTest(needle=needle): + self.assertLess(gate, source.index(needle)) + + def test_runner_default_run_skips_without_hardware(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env.pop("TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE", None) + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("skipped", result.stdout) + self.assertNotIn("TILEXR_CCU_PROBE_RANK=0", result.stdout) + self.assertNotIn("ccu_rank0.log", result.stdout) + + def test_runner_defaults_prepare_failure_fast_exit_and_allows_opt_out(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn( + '"TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE=${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE:-1}"', + source, + ) + self.assertNotIn("TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE=0", source) + + def test_runner_dry_run_shows_rank_specific_endpoint_overrides_common(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE"] = "1" + env["TILEXR_CCU_DIRECT_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN"] = "99" + env["TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN_RANK0"] = "100" + env["TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN_RANK1"] = "200" + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_smoke_runner dryRun=1", result.stdout) + self.assertIn("dryRun rank0 TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN=100", result.stdout) + self.assertIn("dryRun rank1 TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN=200", result.stdout) + self.assertNotIn("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN=99", result.stdout) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + self.assertNotIn("ccu_rank0.log", result.stdout) + + def test_runner_dry_run_shows_rank_specific_resource_window_token_overrides_common(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE"] = "1" + env["TILEXR_CCU_DIRECT_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID"] = "0x1111" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK0"] = "0x2222" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK1"] = "0x3333" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID_RANK1"] = "0x4444" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_RANK1"] = "0x5555" + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_smoke_runner dryRun=1", result.stdout) + self.assertIn("dryRun rank0 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID=0x2222", result.stdout) + self.assertIn("dryRun rank1 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID=0x3333", result.stdout) + self.assertIn("dryRun rank1 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID=0x4444", result.stdout) + self.assertIn("dryRun rank1 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE=0x5555", result.stdout) + self.assertNotIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID=0x1111", result.stdout) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + self.assertNotIn("ccu_rank0.log", result.stdout) + + def test_runner_dry_run_shows_repository_install_diagnostic_variants(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE"] = "1" + env["TILEXR_CCU_DIRECT_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW"] = "full_repository" + env["TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE"] = "descriptor_bytes" + env["TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE"] = "acl_hccl_module" + env["TILEXR_CCU_DIRECT_INSTALL_ORDER"] = "lower_layer_first" + env["TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START"] = "489" + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("dryRun TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW=full_repository", result.stdout) + self.assertIn("dryRun TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE=descriptor_bytes", result.stdout) + self.assertIn("dryRun TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE=acl_hccl_module", result.stdout) + self.assertIn("dryRun TILEXR_CCU_DIRECT_INSTALL_ORDER=lower_layer_first", result.stdout) + self.assertIn("dryRun TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START=489", result.stdout) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + self.assertNotIn("ccu_rank0.log", result.stdout) + + def test_runner_dry_run_defaults_to_lower_layer_first_install_order(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE"] = "1" + env["TILEXR_CCU_DIRECT_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env.pop("TILEXR_CCU_DIRECT_INSTALL_ORDER", None) + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("dryRun TILEXR_CCU_DIRECT_INSTALL_ORDER=lower_layer_first", result.stdout) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + + def test_runner_dry_run_defaults_sync_xn_post_only_window_for_hcomm_style_task1_prelude(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE"] = "1" + env["TILEXR_CCU_DIRECT_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_BARRIER_MODE"] = "sync_xn_post_only" + env["TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT"] = "1" + env["TILEXR_CCU_PROBE_INSTRUCTION_START"] = "475" + env["TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START"] = "489" + env.pop("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT", None) + result = subprocess.run( + ["bash", str(RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("dryRun TILEXR_CCU_DIRECT_BARRIER_MODE=sync_xn_post_only", result.stdout) + self.assertIn("dryRun derived task0.instStartId=489 task0.instCnt=13", result.stdout) + self.assertIn("dryRun derived task1.instStartId=502 task1.instCnt=6", result.stdout) + self.assertIn( + "dryRun derived repositoryStartId=475 repositoryCount=33 missionInstructionStartId=489 " + "missionInstructionCount=19", + result.stdout, + ) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + + def test_npu_busy_guard_rejects_selected_device_processes(self): + sample = """ ++------------------+---------------+--------------+------------------+ +| NPU Chip | Process id | Process name | Process memory | ++==================+===============+==============+==================+ +| 0 0 | 31415 | python3.10 | 1024 | +| 2 0 | 27182 | train.py | 2048 | ++------------------+---------------+--------------+------------------+ +""" + with tempfile.TemporaryDirectory() as temp_dir: + log_path = Path(temp_dir) / "npu-smi.log" + log_path.write_text(sample, encoding="utf-8") + busy = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "0,1"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + idle = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "1"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + + self.assertEqual(1, busy.returncode, busy.stdout + busy.stderr) + self.assertIn("device=0 pid=31415 process=python3.10", busy.stdout) + self.assertEqual(0, idle.returncode, idle.stdout + idle.stderr) + self.assertIn("no selected NPU processes", idle.stdout) + + def test_npu_busy_guard_ignores_device_status_table(self): + sample = """ ++------+-------------+--------+-------------+ +| NPU | Name | Health | Power(W) | ++======+=============+========+=============+ +| 0 | Ascend950PR | OK | 95 | +| 1 | Ascend950PR | OK | 93 | ++------+-------------+--------+-------------+ +""" + with tempfile.TemporaryDirectory() as temp_dir: + log_path = Path(temp_dir) / "npu-smi.log" + log_path.write_text(sample, encoding="utf-8") + result = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "0,1"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("no selected NPU processes", result.stdout) + + def test_npu_busy_guard_rejects_selected_alarm_devices(self): + sample = """ ++------+-------------+--------+-------------+ +| NPU | Name | Health | Power(W) | ++======+=============+========+=============+ +| 0 | Ascend950PR | Alarm | 95 | +| 1 | Ascend950PR | OK | 93 | +| 2 | Ascend950PR | Alarm | 94 | ++------+-------------+--------+-------------+ +""" + with tempfile.TemporaryDirectory() as temp_dir: + log_path = Path(temp_dir) / "npu-smi.log" + log_path.write_text(sample, encoding="utf-8") + unhealthy = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "0,1"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + healthy = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "1"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + + self.assertEqual(1, unhealthy.returncode, unhealthy.stdout + unhealthy.stderr) + self.assertIn("unhealthy selected NPU device=0 health=Alarm", unhealthy.stdout) + self.assertNotIn("device=2", unhealthy.stdout) + self.assertEqual(0, healthy.returncode, healthy.stdout + healthy.stderr) + self.assertIn("selected NPU devices healthy", healthy.stdout) + + def test_npu_busy_guard_can_allow_alarm_without_allowing_busy_processes(self): + sample = """ ++------+-------------+--------+-------------+ +| NPU | Name | Health | Power(W) | ++======+=============+========+=============+ +| 0 | Ascend950PR | Alarm | 95 | +| 1 | Ascend950PR | OK | 93 | +| 2 | Ascend950PR | Alarm | 94 | ++------+-------------+--------+-------------+ +| NPU Chip | Process id | Process name | Process memory | ++==================+===============+==============+==================+ +| 0 0 | 31415 | python3.10 | 1024 | ++------+-------------+--------+-------------+ +""" + with tempfile.TemporaryDirectory() as temp_dir: + log_path = Path(temp_dir) / "npu-smi.log" + log_path.write_text(sample, encoding="utf-8") + allowed_health = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "2", "--allow-unhealthy"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + busy_still_blocked = subprocess.run( + ["python", str(BUSY_GUARD), "--log", str(log_path), "--devices", "0", "--allow-unhealthy"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + + self.assertEqual(0, allowed_health.returncode, allowed_health.stdout + allowed_health.stderr) + self.assertIn("unhealthy selected NPU devices allowed", allowed_health.stdout) + self.assertNotIn("selected NPU devices healthy", allowed_health.stdout) + self.assertEqual(1, busy_still_blocked.returncode, busy_still_blocked.stdout + busy_still_blocked.stderr) + self.assertIn("device=0 pid=31415 process=python3.10", busy_still_blocked.stdout) + + def test_runner_requires_submit_for_barrier_wait_expectation(self): + source = RUNNER.read_text(encoding="utf-8") + barrier_gate = source.index('if [ "${TILEXR_CCU_DIRECT_SMOKE_EXPECT_BARRIER_WAIT:-0}" = "1" ]') + submit_guard = source.index( + 'if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" != "1" ]; then', + barrier_gate, + ) + timing_parse = source.index("wait_sync_ms=", barrier_gate) + + self.assertLess(barrier_gate, submit_guard) + self.assertLess(submit_guard, timing_parse) + self.assertIn("direct CCU barrier wait check requires TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1", source) + + def test_evening_wrapper_defaults_to_process_mode_direct_ccu_only_prepare_first(self): + source = EVENING_RUNNER.read_text(encoding="utf-8") + + self.assertIn('--dry-run', source) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN", source) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES", source) + self.assertIn("source scripts/common_env.sh", source) + self.assertIn("cmake --build build --target tile-comm", source) + self.assertIn("TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1", source) + self.assertIn("TILEXR_CCU_SMOKE_REQUIRE_NPU_SMI=1", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=0", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SUBMIT", source) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT", source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1', source) + self.assertIn("TILEXR_CCU_DIRECT_BARRIER_MODE=sync_cke", source) + self.assertIn("TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE=hcomm_cap", source) + self.assertIn("TILEXR_CCU_PROBE_SQE_ARG_COUNT", source) + self.assertIn("TILEXR_CCU_PROBE_MISSION_START", source) + self.assertIn("TILEXR_CCU_PROBE_INSTRUCTION_START", source) + self.assertIn("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START", source) + self.assertIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE", source) + self.assertIn("TILEXR_CCU_PROBE_RANK0_XN_START", source) + self.assertIn("TILEXR_CCU_PROBE_RANK1_XN_START", source) + self.assertIn(":-475", source) + self.assertIn(":-489", source) + self.assertIn(":-143", source) + self.assertIn(":-full_repository", source) + self.assertIn(":-instruction_bytes", source) + self.assertIn(":-acl", source) + self.assertIn("tests/ccu/run_tilexr_ccu_direct_smoke.sh", source) + self.assertIn('run_smoke_stage "prepare_${safe_prepare_mode}"', source) + self.assertIn("prepare_has_submit_ready", source) + self.assertIn("submitReady=1", source) + self.assertIn("run_smoke_stage submit", source) + self.assertIn("run_smoke_stage barrier", source) + self.assertIn("run_smoke_stage p2p", source) + self.assertLess(source.index('run_smoke_stage "prepare_${safe_prepare_mode}"'), source.index("run_smoke_stage submit")) + self.assertLess(source.index("prepare_has_submit_ready"), source.index("run_smoke_stage submit")) + + def test_evening_wrapper_dry_run_short_circuits_before_common_env(self): + source = EVENING_RUNNER.read_text(encoding="utf-8") + + dry_run_gate = source.index('if [ "${TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN:-0}" = "1" ]') + common_env_source = source.index("source scripts/common_env.sh") + cmake_build = source.index("cmake --build build --target tile-comm") + + self.assertLess(dry_run_gate, common_env_source) + self.assertLess(dry_run_gate, cmake_build) + + def test_evening_wrapper_dry_run_does_not_invoke_fake_npu_smi(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + marker = temp_path / "npu-smi-called" + fake_npu_smi = temp_path / "npu-smi" + fake_npu_smi.write_text( + f"#!/usr/bin/env bash\n" + f"echo called >> {str(marker).replace(os.sep, '/')!r}\n" + f"exit 0\n", + encoding="utf-8", + ) + fake_npu_smi.chmod(0o755) + + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN"] = "1" + env["PATH"] = str(temp_path) + os.pathsep + env.get("PATH", "") + result = subprocess.run( + ["bash", str(EVENING_RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_evening_smoke dryRun=1", result.stdout) + self.assertFalse(marker.exists(), result.stdout + result.stderr) + + def test_evening_wrapper_retries_prepare_with_module3_allocator_before_submit(self): + source = EVENING_RUNNER.read_text(encoding="utf-8") + + self.assertIn('TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES:-acl,acl_module3,rt_hbm', source) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES", source) + self.assertIn('prepare_alloc_modes', source) + self.assertIn('prepare_${safe_prepare_mode}', source) + self.assertIn('TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${prepare_profile_alloc}"', source) + self.assertIn('TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${prepare_profile_window}"', source) + self.assertIn('TILEXR_CCU_DIRECT_REPOSITORY_DATA_LEN_MODE="${prepare_profile_data_len_mode}"', source) + self.assertIn('TILEXR_CCU_DIRECT_INSTALL_ORDER="${prepare_profile_install_order}"', source) + self.assertIn('selected_prepare_alloc_mode', source) + self.assertIn('selected_prepare_window', source) + self.assertIn('prepareStatus=', source) + self.assertIn('prepare_status_summary', source) + self.assertIn('selectedPrepare alloc=', source) + self.assertIn('stopAfter=prepare reason="submitReady=1 missing for every prepare profile"', source) + + prepare_loop = source.index('for prepare_profile in "${prepare_profiles[@]}"') + self.assertLess( + source.index('TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${prepare_profile_alloc}"', prepare_loop), + source.index("prepare_has_submit_ready", prepare_loop), + ) + self.assertLess(source.index("selected_prepare_alloc_mode"), source.index("run_smoke_stage submit")) + submit_stage = source.index("run_smoke_stage submit") + self.assertLess( + submit_stage, + source.index('TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}"', submit_stage), + ) + self.assertLess( + submit_stage, + source.index('TILEXR_CCU_DIRECT_REPOSITORY_INSTALL_WINDOW="${selected_prepare_window}"', submit_stage), + ) + barrier_stage = source.index("run_smoke_stage barrier") + self.assertLess( + barrier_stage, + source.index('TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}"', barrier_stage), + ) + p2p_stage = source.index("run_smoke_stage p2p") + self.assertLess( + p2p_stage, + source.index('TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE="${selected_prepare_alloc_mode}"', p2p_stage), + ) + + def test_evening_wrapper_dry_run_lists_prepare_profiles(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES"] = ( + "full_acl:acl:full_repository:instruction_bytes:lower_layer_first," + "mission_desc:acl:mission:descriptor_bytes:repository_first" + ) + result = subprocess.run( + ["bash", str(EVENING_RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn( + "TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES=" + "full_acl:acl:full_repository:instruction_bytes:lower_layer_first," + "mission_desc:acl:mission:descriptor_bytes:repository_first", + result.stdout, + ) + self.assertIn( + "dryRun prepareProfile[0] name=full_acl alloc=acl window=full_repository " + "dataLenMode=instruction_bytes installOrder=lower_layer_first", + result.stdout, + ) + self.assertIn( + "dryRun prepareProfile[1] name=mission_desc alloc=acl window=mission " + "dataLenMode=descriptor_bytes installOrder=repository_first", + result.stdout, + ) + + def test_evening_wrapper_prepare_profiles_pass_repository_options_to_runner(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cmake = fake_bin / "cmake" + fake_cmake.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_cmake.chmod(0o755) + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\n" + "shift\n" + "exec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_repo = temp_path / "repo" + fake_tests = fake_repo / "tests" / "ccu" + fake_scripts = fake_repo / "scripts" + fake_tests.mkdir(parents=True) + fake_scripts.mkdir() + (fake_scripts / "common_env.sh").write_text("#!/usr/bin/env bash\n", encoding="utf-8") + fake_runner = fake_tests / "run_tilexr_ccu_direct_smoke.sh" + fake_runner.write_text( + "#!/usr/bin/env bash\n" + "mkdir -p \"${TILEXR_CCU_SMOKE_WORK_DIR}\"\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" < \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" < \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" < \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" < \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submit ret=0 submitted=1 taskCount=1 submittedTaskCount=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submitTiming rank=0 preSubmitDelayMs=0 submitRet=0 syncRet=0 submitMs=1 syncMs=150\n" + "tilexr_ccu_direct_smoke p2pCcuCopy rank=0 passed=1\n" + "LOG\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submit ret=0 submitted=1 taskCount=1 submittedTaskCount=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submitTiming rank=1 preSubmitDelayMs=0 submitRet=0 syncRet=0 submitMs=1 syncMs=160\n" + "tilexr_ccu_direct_smoke p2pCcuCopy rank=1 passed=1\n" + "LOG\n" + "exit 0\n", + encoding="utf-8", + ) + fake_runner.chmod(0o755) + fake_evening = fake_tests / "run_tilexr_ccu_direct_evening_smoke.sh" + fake_evening.write_text(EVENING_RUNNER.read_text(encoding="utf-8"), encoding="utf-8") + fake_evening.chmod(0o755) + + env = os.environ.copy() + env["PATH"] = str(fake_bin) + os.pathsep + env.get("PATH", "") + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES"] = "prepare,submit,barrier,p2p" + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_PROFILES"] = ( + "pfe_hcomm:acl:full_repository:instruction_bytes:lower_layer_first:hcomm_die:hcomm_fe_id" + ) + env["TILEXR_CCU_EVENING_WORK_ROOT"] = "work" + result = subprocess.run( + ["bash", str(fake_evening)], + cwd=fake_repo, + check=False, + text=True, + capture_output=True, + env=env, + ) + final_status = fake_repo / "work" / "final_status.log" + final_status_exists = final_status.exists() + final_status_text = final_status.read_text(encoding="utf-8") if final_status_exists else "" + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn( + "finalStatus prepare=pass submit=pass barrier=pass p2p=pass completionCandidate=1 failedStage=none selectedProfile=pfe_hcomm", + result.stdout, + ) + self.assertTrue(final_status_exists, result.stdout + result.stderr) + self.assertIn( + "finalStatus prepare=pass submit=pass barrier=pass p2p=pass completionCandidate=1 failedStage=none selectedProfile=pfe_hcomm", + final_status_text, + ) + self.assertIn("selectedPrepare", result.stdout) + self.assertIn("success workRoot=work", result.stdout) + + def test_evening_wrapper_five_field_profile_preserves_ambient_pfe_layout(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cmake = fake_bin / "cmake" + fake_cmake.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_cmake.chmod(0o755) + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\n" + "shift\n" + "exec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_repo = temp_path / "repo" + fake_tests = fake_repo / "tests" / "ccu" + fake_scripts = fake_repo / "scripts" + fake_tests.mkdir(parents=True) + fake_scripts.mkdir() + (fake_scripts / "common_env.sh").write_text("#!/usr/bin/env bash\n", encoding="utf-8") + fake_runner = fake_tests / "run_tilexr_ccu_direct_smoke.sh" + fake_runner.write_text( + "#!/usr/bin/env bash\n" + "mkdir -p \"${TILEXR_CCU_SMOKE_WORK_DIR}\"\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" < \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke config rank=0 installOrder=1\n" + "tilexr_ccu_direct_smoke prepare ret=6 submitReady=0 message=\"failed to install CCU repository instruction image: CCU custom channel call failed op=251 driverRet=7 opRet=9\"\n" + "tilexr_ccu_direct_smoke preparedTasks count=0\n" + "LOG\n" + "exit 6\n", + encoding="utf-8", + ) + fake_runner.chmod(0o755) + fake_evening = fake_tests / "run_tilexr_ccu_direct_evening_smoke.sh" + fake_evening.write_text(EVENING_RUNNER.read_text(encoding="utf-8"), encoding="utf-8") + fake_evening.chmod(0o755) + + env = os.environ.copy() + env["PATH"] = str(fake_bin) + os.pathsep + env.get("PATH", "") + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES"] = "prepare" + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES"] = "acl" + env["TILEXR_CCU_EVENING_WORK_ROOT"] = "work" + result = subprocess.run( + ["bash", str(fake_evening)], + cwd=fake_repo, + check=False, + text=True, + capture_output=True, + env=env, + ) + final_status = fake_repo / "work" / "final_status.log" + final_status_exists = final_status.exists() + final_status_text = final_status.read_text(encoding="utf-8") if final_status_exists else "" + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("prepareLogSummary mode=acl status=6", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke prepare ret=6 submitReady=0", result.stdout) + self.assertIn("CCU custom channel call failed op=251 driverRet=7 opRet=9", result.stdout) + self.assertIn('stopAfter=prepare reason="submitReady=1 missing for every prepare profile"', result.stdout) + self.assertIn( + "finalStatus prepare=fail submit=skipped barrier=skipped p2p=skipped " + "completionCandidate=0 failedStage=prepare", + result.stdout, + ) + self.assertTrue(final_status_exists, result.stdout + result.stderr) + self.assertIn( + "finalStatus prepare=fail submit=skipped barrier=skipped p2p=skipped " + "completionCandidate=0 failedStage=prepare", + final_status_text, + ) + + def test_evening_wrapper_extracts_submit_stage_summary_from_logs(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cmake = fake_bin / "cmake" + fake_cmake.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_cmake.chmod(0o755) + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\n" + "shift\n" + "exec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_repo = temp_path / "repo" + fake_tests = fake_repo / "tests" / "ccu" + fake_scripts = fake_repo / "scripts" + fake_tests.mkdir(parents=True) + fake_scripts.mkdir() + (fake_scripts / "common_env.sh").write_text("#!/usr/bin/env bash\n", encoding="utf-8") + fake_runner = fake_tests / "run_tilexr_ccu_direct_smoke.sh" + fake_runner.write_text( + "#!/usr/bin/env bash\n" + "mkdir -p \"${TILEXR_CCU_SMOKE_WORK_DIR}\"\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "tilexr_ccu_direct_smoke submit ret=0 submitted=1 taskCount=1 submittedTaskCount=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submitTiming rank=0 preSubmitDelayMs=0 submitRet=0 syncRet=0 submitMs=1 syncMs=7\n" + "LOG\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "tilexr_ccu_direct_smoke submit ret=0 submitted=1 taskCount=1 submittedTaskCount=1 message=\"ok\"\n" + "tilexr_ccu_direct_smoke submitTiming rank=1 preSubmitDelayMs=0 submitRet=0 syncRet=0 submitMs=1 syncMs=8\n" + "LOG\n" + "exit 0\n", + encoding="utf-8", + ) + fake_runner.chmod(0o755) + fake_evening = fake_tests / "run_tilexr_ccu_direct_evening_smoke.sh" + fake_evening.write_text(EVENING_RUNNER.read_text(encoding="utf-8"), encoding="utf-8") + fake_evening.chmod(0o755) + + env = os.environ.copy() + env["PATH"] = str(fake_bin) + os.pathsep + env.get("PATH", "") + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES"] = "prepare,submit" + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES"] = "acl" + env["TILEXR_CCU_EVENING_WORK_ROOT"] = "work" + result = subprocess.run( + ["bash", str(fake_evening)], + cwd=fake_repo, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("stageLogSummary stage=submit status=0", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke submit ret=0 submitted=1", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke submitTiming rank=1", result.stdout) + + def test_evening_wrapper_runs_p2p_ccu_copy_with_process_mode_direct_ccu_init(self): + source = EVENING_RUNNER.read_text(encoding="utf-8") + p2p_stage = source[source.index("if stage_enabled p2p;"):] + p2p_stage = p2p_stage[:p2p_stage.index("print_final_status none")] + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY=1", p2p_stage) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY=1", p2p_stage) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=1", p2p_stage) + self.assertNotIn("TILEXR_CCU_DIRECT_SMOKE_THREAD_MODE=1", p2p_stage) + self.assertNotIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=0", p2p_stage) + + def test_evening_wrapper_keeps_submit_failure_diagnostics_before_long_trace_tail(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cmake = fake_bin / "cmake" + fake_cmake.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_cmake.chmod(0o755) + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\n" + "shift\n" + "exec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_repo = temp_path / "repo" + fake_tests = fake_repo / "tests" / "ccu" + fake_scripts = fake_repo / "scripts" + fake_tests.mkdir(parents=True) + fake_scripts.mkdir() + (fake_scripts / "common_env.sh").write_text("#!/usr/bin/env bash\n", encoding="utf-8") + fake_runner = fake_tests / "run_tilexr_ccu_direct_smoke.sh" + fake_runner.write_text( + "#!/usr/bin/env bash\n" + "mkdir -p \"${TILEXR_CCU_SMOKE_WORK_DIR}\"\n" + "if [[ \"${TILEXR_CCU_SMOKE_WORK_DIR}\" != *submit* ]]; then\n" + " cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "LOG\n" + " cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "LOG\n" + " exit 0\n" + "fi\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "tilexr_ccu_direct_smoke submit ret=-2 submitted=0 taskCount=1 submittedTaskCount=0 message=\"direct CCU submit failed task=0 ret=-2 rtRet=507000 dieId=1 missionId=6 instStartId=489 instCnt=156 key=0x59b0f03 argSize=13 args[0]=0xfeed\"\n" + "LOG\n" + "for i in $(seq 1 40); do\n" + " echo \"TileXRDirectCcuTrace program.sync[$i] decoded=SyncXn\" >> \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\"\n" + "done\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "LOG\n" + "exit 7\n", + encoding="utf-8", + ) + fake_runner.chmod(0o755) + fake_evening = fake_tests / "run_tilexr_ccu_direct_evening_smoke.sh" + fake_evening.write_text(EVENING_RUNNER.read_text(encoding="utf-8"), encoding="utf-8") + fake_evening.chmod(0o755) + + env = os.environ.copy() + env["PATH"] = str(fake_bin) + os.pathsep + env.get("PATH", "") + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES"] = "prepare,submit" + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES"] = "acl" + env["TILEXR_CCU_EVENING_WORK_ROOT"] = "work" + result = subprocess.run( + ["bash", str(fake_evening)], + cwd=fake_repo, + check=False, + text=True, + capture_output=True, + env=env, + ) + final_status = fake_repo / "work" / "final_status.log" + final_status_exists = final_status.exists() + final_status_text = final_status.read_text(encoding="utf-8") if final_status_exists else "" + + self.assertEqual(7, result.returncode, result.stdout + result.stderr) + self.assertIn("stageLogSummary stage=submit status=7", result.stdout) + self.assertIn("direct CCU submit failed task=0", result.stdout) + self.assertIn("rtRet=507000", result.stdout) + self.assertIn("args[0]=0xfeed", result.stdout) + self.assertIn( + "finalStatus prepare=pass submit=fail barrier=skipped p2p=skipped completionCandidate=0 failedStage=submit", + result.stdout, + ) + self.assertTrue(final_status_exists, result.stdout + result.stderr) + self.assertIn( + "finalStatus prepare=pass submit=fail barrier=skipped p2p=skipped completionCandidate=0 failedStage=submit", + final_status_text, + ) + + def test_evening_wrapper_stage_summary_keeps_decoded_direct_trace_lines(self): + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cmake = fake_bin / "cmake" + fake_cmake.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") + fake_cmake.chmod(0o755) + fake_timeout = fake_bin / "timeout" + fake_timeout.write_text( + "#!/usr/bin/env bash\n" + "shift\n" + "exec \"$@\"\n", + encoding="utf-8", + ) + fake_timeout.chmod(0o755) + + fake_repo = temp_path / "repo" + fake_tests = fake_repo / "tests" / "ccu" + fake_scripts = fake_repo / "scripts" + fake_tests.mkdir(parents=True) + fake_scripts.mkdir() + (fake_scripts / "common_env.sh").write_text("#!/usr/bin/env bash\n", encoding="utf-8") + fake_runner = fake_tests / "run_tilexr_ccu_direct_smoke.sh" + fake_runner.write_text( + "#!/usr/bin/env bash\n" + "mkdir -p \"${TILEXR_CCU_SMOKE_WORK_DIR}\"\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank0.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "TileXRDirectCcuTrace lowerLayerPfe[0] decoded=PfeCtx pfeOffset=18 startTaJettyId=1024\n" + "TileXRDirectCcuTrace lowerLayerJettyCtx[0,0] decoded=LocalJettyCtx inferredSqDepth=64 wqeBasicBlockStartId=0\n" + "TileXRDirectCcuTrace lowerLayerChannel[0] decoded=ChannelCtxV1 sourcePfeId=2 remoteCcuVa=0x12340000\n" + "TileXRDirectCcuTrace remoteXnBinding[0] localXn=1961 remoteXn=2361 endpointRouteVerified=1\n" + "TileXRDirectCcuTrace task[0] missionId=6 instStartId=489 instCnt=13 argSize=13\n" + "TileXRDirectCcuTrace task[1] missionId=6 instStartId=502 instCnt=143 argSize=13\n" + "TileXRDirectCcuTrace finalRuntimeTask[0] dieId=1 missionId=6 timeout=20 instStartId=489 instCnt=2 key=0x59b0f03 argSize=1 args[0]=0xabc000\n" + "TileXRDirectCcuTrace customChannel.return op=251 driverRet=328107 opRet=0\n" + "LOG\n" + "cat > \"${TILEXR_CCU_SMOKE_WORK_DIR}/ccu_rank1.log\" <<'LOG'\n" + "tilexr_ccu_direct_smoke prepare ret=0 installSucceeded=1 submitReady=1\n" + "LOG\n" + "exit 0\n", + encoding="utf-8", + ) + fake_runner.chmod(0o755) + fake_evening = fake_tests / "run_tilexr_ccu_direct_evening_smoke.sh" + fake_evening.write_text(EVENING_RUNNER.read_text(encoding="utf-8"), encoding="utf-8") + fake_evening.chmod(0o755) + + env = os.environ.copy() + env["PATH"] = str(fake_bin) + os.pathsep + env.get("PATH", "") + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES"] = "prepare" + env["TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES"] = "acl" + env["TILEXR_CCU_EVENING_WORK_ROOT"] = "work" + result = subprocess.run( + ["bash", str(fake_evening)], + cwd=fake_repo, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("decoded=PfeCtx", result.stdout) + self.assertIn("decoded=LocalJettyCtx", result.stdout) + self.assertIn("decoded=ChannelCtxV1", result.stdout) + self.assertIn("remoteXnBinding[0]", result.stdout) + self.assertIn("TileXRDirectCcuTrace task[0]", result.stdout) + self.assertIn("TileXRDirectCcuTrace finalRuntimeTask[0]", result.stdout) + self.assertIn("TileXRDirectCcuTrace customChannel.return op=251", result.stdout) + + def test_evening_wrapper_dry_run_does_not_touch_npu(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_EVENING_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK0"] = "0x2222" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK1"] = "0x3333" + result = subprocess.run( + ["bash", str(EVENING_RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_evening_smoke dryRun=1", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DRY_RUN=1", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_SMOKE_STAGES=prepare,submit,barrier,p2p", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_EVENING_PREPARE_ALLOC_MODES=acl,acl_module3,rt_hbm", result.stdout) + self.assertIn( + "dryRun prepareProfile[2] name=rt_hbm alloc=rt_hbm window=full_repository", + result.stdout, + ) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=1", result.stdout) + self.assertIn("TILEXR_CCU_PROBE_INSTRUCTION_START=475", result.stdout) + self.assertIn("TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START=489", result.stdout) + self.assertIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT=143", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_REPOSITORY_MEMORY_ALLOC_MODE=acl", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_ORDER=lower_layer_first", result.stdout) + self.assertIn("dryRun TILEXR_CCU_DIRECT_INSTALL_ORDER=lower_layer_first", result.stdout) + self.assertIn("TILEXR_CCU_PROBE_SQE_ARG_COUNT=13", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_BARRIER_MODE=sync_cke", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE=hcomm_cap", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK0=0x2222", result.stdout) + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK1=0x3333", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke_runner dryRun=1", result.stdout) + self.assertIn("dryRun rank0 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID=0x2222", result.stdout) + self.assertIn("dryRun rank1 TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID=0x3333", result.stdout) + self.assertIn( + "dryRun derived repositoryStartId=475 repositoryCount=170 missionInstructionStartId=489", + result.stdout, + ) + self.assertIn("dryRun derived task0.instStartId=489 task0.instCnt=13", result.stdout) + self.assertIn("dryRun derived task1.instStartId=502 task1.instCnt=143", result.stdout) + self.assertIn("dryRun derived SET_INSTRUCTION offsetStartIdx=475 dataLen=5440", result.stdout) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + + def test_evening_wrapper_accepts_dry_run_argument_without_touching_npu(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + result = subprocess.run( + ["bash", str(EVENING_RUNNER), "--dry-run"], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_evening_smoke dryRun=1", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke_runner dryRun=1", result.stdout) + self.assertNotIn("cmake --build", result.stdout + result.stderr) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + + def test_evening_wrapper_accepts_generic_smoke_dry_run_without_touching_npu(self): + with tempfile.TemporaryDirectory() as temp_dir: + env = os.environ.copy() + env["TILEXR_CCU_SMOKE_DRY_RUN"] = "1" + env["TILEXR_CCU_SMOKE_WORK_DIR"] = temp_dir + result = subprocess.run( + ["bash", str(EVENING_RUNNER)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("tilexr_ccu_direct_evening_smoke dryRun=1", result.stdout) + self.assertIn("tilexr_ccu_direct_smoke_runner dryRun=1", result.stdout) + self.assertNotIn("stage=prepare", result.stdout + result.stderr) + self.assertNotIn("npu-smi rc=", result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_driver_adapter.py b/tests/ccu/test_tilexr_ccu_driver_adapter.py new file mode 100644 index 0000000..cf8fc80 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_driver_adapter.py @@ -0,0 +1,992 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +import os +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +DRIVER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.h" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" +SPECS_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.h" +SPECS_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuDriverAdapterTest(unittest.TestCase): + def compile_and_run(self, code: str, extra_env=None): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "driver_adapter_test.cpp" + test_bin = temp_path / "driver_adapter_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(DRIVER_SOURCE), + str(SPECS_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + env = None + if extra_env: + env = {**os.environ, **extra_env} + return subprocess.run( + [str(test_bin)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env) + + def test_adapter_wraps_get_basic_info_and_reuses_tilexr_specs_decode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + if (out == nullptr) { + return -1; + } + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x45; + out->data.dataInfo.dataArray[0].baseinfo.tokenId = 0x1234; + out->data.dataInfo.dataArray[0].baseinfo.tokenValue = 0; + out->data.dataInfo.dataArray[0].baseinfo.tokenValid = 1; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x059b0f03U; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x200000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (3U << 24) | (5U << 16) | 255U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (127U << 16) | 63U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (31U << 16) | 15U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (7U << 16) | 1U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap4 = 9U; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(4, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(1, &basic, &report) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 4 || + state.observedOp != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.observedDie != 1) { + std::cerr << "custom channel request mismatch\n"; + return 3; + } + if (basic.dieId != 1 || basic.msId != 0x45 || basic.missionKey != 0x059b0f03U || + basic.resourceAddr != 0x200000000ULL || basic.caps.cap0 == 0 || + basic.msidToken.tokenId != 0x1234 || basic.msidToken.tokenValue != 0 || + !basic.msidToken.valid) { + std::cerr << "basic info mismatch\n"; + return 4; + } + + TileXRCcuSpecInfo info; + TileXRCcuSpecsReport specsReport; + if (TileXRCcuDecodeBasicInfo(basic, &info, &specsReport) != TILEXR_SUCCESS) { + std::cerr << "decode failed: " << specsReport.message << "\n"; + return 5; + } + if (info.instructionNum != 256 || info.xnNum != 128 || info.channelNum != 2 || + info.missionNum != 6 || info.loopEngineNum != 4) { + std::cerr << "decoded info mismatch\n"; + return 6; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_GET_BASIC_INFO || + report.dieId != 1 || report.devicePhyId != 4) { + std::cerr << "report mismatch\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_wraps_die_enable_and_reports_driver_errors(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + bool fail = false; + uint32_t observedOp = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOp = in.op; + if (state->fail) { + return -22; + } + out->opRet = 0; + out->data.dataInfo.dataArray[0].dieinfo.enableFlag = TILEXR_CCU_ENABLE_FLAG; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(7, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed\n"; + return 1; + } + + bool enabled = false; + if (adapter.GetDieEnabled(0, &enabled, &report) != TILEXR_SUCCESS || !enabled) { + std::cerr << "die enable query failed: " << report.message << "\n"; + return 2; + } + if (state.observedOp != TILEXR_CCU_U_OP_GET_DIE_WORKING) { + std::cerr << "die opcode mismatch\n"; + return 3; + } + + state.fail = true; + if (adapter.GetDieEnabled(0, &enabled, &report) != TILEXR_ERROR_MKIRT) { + std::cerr << "driver failure was accepted\n"; + return 4; + } + if (report.message.find("CCU custom channel call failed") == std::string::npos || + report.message.find("driverRet=-22") == std::string::npos || + report.message.find("opRet=0") == std::string::npos || + report.message.find("op=15") == std::string::npos || + report.driverRet != -22) { + std::cerr << "weak driver diagnostic: " << report.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_installs_instruction_repository_with_set_instruction_opcode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint32_t observedArraySize = 0; + uint64_t observedResourceAddr = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedArraySize = in.data.dataInfo.dataArraySize; + state->observedResourceAddr = in.data.dataInfo.dataArray[0].insinfo.resourceAddr; + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + if (adapter.InstallInstructions(1, 489, 13, 0x100051152e00ULL, 13 * 32, &report) != + TILEXR_SUCCESS) { + std::cerr << "install failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 5 || + state.observedOp != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.observedDie != 1 || state.observedOffset != 489 || + state.observedDataLen != 13 * 32 || state.observedArraySize != 1 || + state.observedResourceAddr != 0x100051152e00ULL) { + std::cerr << "SET_INSTRUCTION request mismatch\n"; + return 3; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_SET_INSTRUCTION || + report.dieId != 1 || report.devicePhyId != 5) { + std::cerr << "report mismatch\n"; + return 4; + } + + if (adapter.InstallInstructions(1, 489, 13, 0, 13 * 32, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "zero device instruction address accepted\n"; + return 5; + } + if (adapter.InstallInstructions(1, 489, 0, 0x100051152e00ULL, 0, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty instruction image accepted\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_rejects_instruction_byte_mismatch_before_custom_channel(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + const int ret = adapter.InstallInstructions( + 1, + 489, + 13, + 0x100051152e00ULL, + 12 * TILEXR_CCU_INSTRUCTION_BYTES, + &report); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "byte mismatch accepted ret=" << ret << "\n"; + return 2; + } + if (state.calls != 0 || + report.message.find("byte size mismatch") == std::string::npos) { + std::cerr << "byte mismatch should fail before custom channel: " + << report.message << " calls=" << state.calls << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_reads_each_instruction_from_its_own_data_array_slot(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct InstructionWords { + uint64_t words[4]; + }; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint32_t observedArraySize = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedArraySize = in.data.dataInfo.dataArraySize; + out->opRet = 0; + + InstructionWords first {{0x1014c00010802ULL, 0, 0, 0}}; + InstructionWords second {{0x10804ULL, 0x1014cULL, 0, 0}}; + std::memcpy(out->data.dataInfo.dataArray[0].byte32.raw, &first, sizeof(first)); + std::memcpy(out->data.dataInfo.dataArray[1].byte32.raw, &second, sizeof(second)); + out->data.dataInfo.dataArraySize = 2; + out->data.dataInfo.dataLen = 2 * TILEXR_CCU_INSTRUCTION_BYTES; + out->offsetNextIdx = 491; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + InstructionWords instructions[2] {}; + if (adapter.ReadInstructions( + 1, + 489, + instructions, + 2, + 2 * TILEXR_CCU_INSTRUCTION_BYTES, + &report) != TILEXR_SUCCESS) { + std::cerr << "read failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 5 || + state.observedOp != TILEXR_CCU_U_OP_GET_INSTRUCTION || + state.observedDie != 1 || state.observedOffset != 489 || + state.observedDataLen != 2 * TILEXR_CCU_INSTRUCTION_BYTES || + state.observedArraySize != 2) { + std::cerr << "GET_INSTRUCTION request mismatch\n"; + return 3; + } + if (instructions[0].words[0] != 0x1014c00010802ULL || + instructions[0].words[1] != 0 || + instructions[1].words[0] != 0x10804ULL || + instructions[1].words[1] != 0x1014cULL) { + std::cerr << "readback slot copy mismatch first=0x" << std::hex + << instructions[0].words[0] << " second0=0x" + << instructions[1].words[0] << " second1=0x" + << instructions[1].words[1] << "\n"; + return 4; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_GET_INSTRUCTION || + report.dieId != 1 || report.devicePhyId != 5) { + std::cerr << "report mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_set_instruction_trailer_wire_word_is_offset_then_opcode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct FakeState { + uint64_t trailer = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + std::memcpy(&state->trailer, &in.offsetStartIdx, sizeof(state->trailer)); + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + if (adapter.InstallInstructions( + 1, 489, 13, 0x100051152e00ULL, 13 * TILEXR_CCU_INSTRUCTION_BYTES, &report) != + TILEXR_SUCCESS) { + std::cerr << "install failed: " << report.message << "\n"; + return 2; + } + const uint64_t expected = + (static_cast(TILEXR_CCU_U_OP_SET_INSTRUCTION) << 32U) | 489ULL; + if (state.trailer != expected) { + std::cerr << "trailer mismatch observed=0x" << std::hex << state.trailer + << " expected=0x" << expected << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_wraps_lower_layer_set_payloads_without_hcomm_runtime(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t die = 0; + uint32_t offset = 0; + uint32_t dataLen = 0; + uint32_t arraySize = 0; + uint8_t raw[256] = {0}; + uint32_t msId = 0; + uint32_t tokenId = 0; + uint32_t tokenValue = 0; + }; + + struct FakeState { + int calls = 0; + ObservedCall observed[8]; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + if (state->calls >= 8) { + return -1; + } + auto& observed = state->observed[state->calls++]; + observed.op = in.op; + observed.die = in.data.dataInfo.udieIdx; + observed.offset = in.offsetStartIdx; + observed.dataLen = in.data.dataInfo.dataLen; + observed.arraySize = in.data.dataInfo.dataArraySize; + observed.msId = in.data.dataInfo.dataArray[0].baseinfo.msId; + observed.tokenId = in.data.dataInfo.dataArray[0].baseinfo.tokenId; + observed.tokenValue = in.data.dataInfo.dataArray[0].baseinfo.tokenValue; + std::memcpy(observed.raw, in.data.dataInfo.dataArray, sizeof(observed.raw)); + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + if (adapter.InstallMsidToken(1, 0x45, 0x1234, 0x5678, &report) != TILEXR_SUCCESS) { + std::cerr << "msid install failed: " << report.message << "\n"; + return 2; + } + if (state.observed[0].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.observed[0].die != 1 || state.observed[0].offset != 0 || + state.observed[0].dataLen != 0 || state.observed[0].arraySize != 0 || + state.observed[0].msId != 0x45 || state.observed[0].tokenId != 0x1234 || + state.observed[0].tokenValue != 0x5678) { + std::cerr << "SET_MSID_TOKEN request mismatch\n"; + return 3; + } + + TileXRCcuPfeCtx pfe{}; + for (uint32_t i = 0; i < sizeof(pfe.raw); ++i) { + pfe.raw[i] = static_cast(0xa0 + i); + } + if (adapter.InstallPfeCtx(1, 7, pfe, &report) != TILEXR_SUCCESS) { + std::cerr << "pfe install failed: " << report.message << "\n"; + return 4; + } + if (state.observed[1].op != TILEXR_CCU_U_OP_SET_PFE || + state.observed[1].die != 1 || state.observed[1].offset != 7 || + state.observed[1].dataLen != TILEXR_CCU_PFE_CTX_BYTES || + state.observed[1].arraySize != 1 || + std::memcmp(state.observed[1].raw, pfe.raw, TILEXR_CCU_PFE_CTX_BYTES) != 0) { + std::cerr << "SET_PFE request mismatch\n"; + return 5; + } + + TileXRCcuLocalJettyCtxData jettys[2]{}; + for (uint32_t i = 0; i < sizeof(jettys[0].raw); ++i) { + jettys[0].raw[i] = static_cast(0x10 + i); + jettys[1].raw[i] = static_cast(0x50 + i); + } + if (adapter.InstallJettyCtx(1, 9, jettys, 2, &report) != TILEXR_SUCCESS) { + std::cerr << "jetty install failed: " << report.message << "\n"; + return 6; + } + if (state.observed[2].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.observed[2].die != 1 || state.observed[2].offset != 9 || + state.observed[2].dataLen != 2 * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES || + state.observed[2].arraySize != 2 || + std::memcmp(state.observed[2].raw, jettys[0].raw, TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0 || + std::memcmp( + state.observed[2].raw + TILEXR_CCU_DATA_ARRAY_SLOT_BYTES, + jettys[1].raw, + TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0) { + std::cerr << "SET_JETTY_CTX request mismatch\n"; + return 7; + } + + TileXRCcuChannelCtxDataV1 channel{}; + for (uint32_t i = 0; i < sizeof(channel.raw); ++i) { + channel.raw[i] = static_cast(0xc0 + i); + } + if (adapter.InstallChannelCtxV1(1, 11, channel, &report) != TILEXR_SUCCESS) { + std::cerr << "channel install failed: " << report.message << "\n"; + return 8; + } + if (state.observed[3].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.observed[3].die != 1 || state.observed[3].offset != 11 || + state.observed[3].dataLen != TILEXR_CCU_CHANNEL_CTX_V1_BYTES || + state.observed[3].arraySize != 1 || + std::memcmp(state.observed[3].raw, channel.raw, TILEXR_CCU_CHANNEL_CTX_V1_BYTES) != 0) { + std::cerr << "SET_CHANNEL request mismatch\n"; + return 9; + } + + if (adapter.ClearCkeRange(1, 16, 10, &report) != TILEXR_SUCCESS) { + std::cerr << "cke clear failed: " << report.message << "\n"; + return 10; + } + if (state.observed[4].op != TILEXR_CCU_U_OP_SET_CKE || + state.observed[4].offset != 16 || state.observed[4].arraySize != 8 || + state.observed[4].dataLen != 8 * TILEXR_CCU_CKE_SLOT_BYTES || + state.observed[5].op != TILEXR_CCU_U_OP_SET_CKE || + state.observed[5].offset != 24 || state.observed[5].arraySize != 2 || + state.observed[5].dataLen != 2 * TILEXR_CCU_CKE_SLOT_BYTES) { + std::cerr << "SET_CKE batching mismatch\n"; + return 11; + } + + if (adapter.InstallXnRange(1, 32, 10, &report) != TILEXR_SUCCESS) { + std::cerr << "xn install failed: " << report.message << "\n"; + return 12; + } + if (state.observed[6].op != TILEXR_CCU_U_OP_SET_XN || + state.observed[6].offset != 32 || state.observed[6].arraySize != 8 || + state.observed[6].dataLen != 8 * TILEXR_CCU_XN_SLOT_BYTES || + state.observed[7].op != TILEXR_CCU_U_OP_SET_XN || + state.observed[7].offset != 40 || state.observed[7].arraySize != 2 || + state.observed[7].dataLen != 2 * TILEXR_CCU_XN_SLOT_BYTES) { + std::cerr << "SET_XN batching mismatch\n"; + return 13; + } + + if (adapter.InstallJettyCtx(1, 9, jettys, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.InstallJettyCtx(1, 9, nullptr, 2, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.ClearCkeRange(1, 0, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.InstallXnRange(1, 0, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid lower-layer payload accepted\n"; + return 14; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_msid_token_envelope_intentionally_matches_hcomm_zero_length_reference(self): + hcomm_source = ( + REPO_ROOT + / "3rdparty" + / "hcomm" + / "src" + / "framework" + / "next" + / "comms" + / "ccu" + / "ccu_device" + / "ccu_comp" + / "ccu_comp.cc" + ).read_text(encoding="utf-8") + hcomm_body = hcomm_source[ + hcomm_source.index("HcclResult CcuComponent::ConfigMsIdToken()") : + hcomm_source.index("HcclResult CcuComponent::GetCcuResourceSpaceBufInfo") + ] + source = DRIVER_SOURCE.read_text(encoding="utf-8") + tilexr_body = source[ + source.index("int TileXRCcuDriverAdapter::InstallMsidToken(") : + source.index("int TileXRCcuDriverAdapter::InstallPfeCtx(") + ] + + for needle in [ + "CCU_U_OP_SET_MSID_TOKEN", + "baseinfo.msId", + "baseinfo.tokenId", + "baseinfo.tokenValue", + ]: + with self.subTest(reference=needle): + self.assertIn(needle, hcomm_body) + self.assertIn(needle, tilexr_body) + + self.assertNotIn("dataArraySize", hcomm_body) + self.assertNotIn("dataLen", hcomm_body) + self.assertNotIn("dataArraySize", tilexr_body) + self.assertNotIn("dataLen", tilexr_body) + + def test_direct_trace_dumps_custom_channel_envelope_and_payload_words(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TILEXR_CCU_DIRECT_TRACE", + "TraceCustomChannelRequest", + "TileXRDirectCcuTrace customChannel", + "devicePhyId=", + "op=", + "dieId=", + "offset=", + "dataLen=", + "arraySize=", + "payloadWords=", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_direct_trace_dumps_custom_channel_return_and_trailer_fields(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TraceCustomChannelReturn", + "TileXRDirectCcuTrace customChannel.return", + "driverRet=", + "opRet=", + "offsetNext=", + "customChannel.requestTrailer", + "customChannel.response", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_direct_trace_runtime_emits_custom_channel_request_payload(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + int main() + { + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, nullptr, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + TileXRCcuChannelCtxDataV1 channel{}; + for (uint32_t i = 0; i < TILEXR_CCU_CHANNEL_CTX_V1_BYTES; ++i) { + channel.raw[i] = static_cast(0x10 + i); + } + if (adapter.InstallChannelCtxV1(1, 11, channel, &report) != TILEXR_SUCCESS) { + std::cerr << "channel install failed: " << report.message << "\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code, {"TILEXR_CCU_DIRECT_TRACE": "1"}) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace customChannel", result.stderr) + self.assertIn("devicePhyId=5", result.stderr) + self.assertIn("op=256", result.stderr) + self.assertIn("dieId=1", result.stderr) + self.assertIn("offset=11", result.stderr) + self.assertIn("dataLen=64", result.stderr) + self.assertIn("arraySize=1", result.stderr) + self.assertIn("payloadWords=8", result.stderr) + self.assertIn("customChannel.payloadWords=8", result.stderr) + self.assertIn("w0=0x1716151413121110", result.stderr) + + def test_direct_trace_runtime_emits_custom_channel_return_and_trailer_fields(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->offsetNextIdx = 99; + out->opRet = 7; + out->data.dataInfo.dataArray[0].dieinfo.enableFlag = 0; + return -22; + } + + int main() + { + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, nullptr, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + bool enabled = true; + const int ret = adapter.GetDieEnabled(1, &enabled, &report); + if (ret != TILEXR_ERROR_MKIRT) { + std::cerr << "driver failure was accepted\n"; + return 2; + } + if (report.driverRet != -22 || report.opRet != 7 || + report.opcode != TILEXR_CCU_U_OP_GET_DIE_WORKING) { + std::cerr << "report did not retain driver diagnostics\n"; + return 3; + } + if (report.message.find("driverRet=-22") == std::string::npos || + report.message.find("opRet=7") == std::string::npos || + report.message.find("op=15") == std::string::npos) { + std::cerr << "message did not retain driver diagnostics: " << report.message << "\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code, {"TILEXR_CCU_DIRECT_TRACE": "1"}) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace customChannel.return", result.stderr) + self.assertIn("driverRet=-22", result.stderr) + self.assertIn("opRet=7", result.stderr) + self.assertIn("offsetNext=99", result.stderr) + self.assertIn("op=15", result.stderr) + self.assertIn("customChannel.requestTrailerWords=1", result.stderr) + self.assertIn("customChannel.responseWords=", result.stderr) + + def test_driver_adapter_failure_message_includes_opcode_and_driver_status(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("CcuCustomChannelFailureMessage", source) + self.assertIn('"CCU custom channel call failed"', source) + self.assertIn('"CCU custom channel operation failed"', source) + self.assertIn('" op="', source) + self.assertIn('" driverRet="', source) + self.assertIn('" opRet="', source) + self.assertIn( + "CcuCustomChannelFailureMessage(\"CCU custom channel call failed\", opcode, driverRet, out->opRet)", + source, + ) + self.assertIn( + "CcuCustomChannelFailureMessage(\"CCU custom channel operation failed\", opcode, driverRet, out->opRet)", + source, + ) + + def test_driver_adapter_is_wired_and_does_not_reference_hcomm_runtime_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = DRIVER_HEADER.read_text(encoding="utf-8") + source = DRIVER_SOURCE.read_text(encoding="utf-8") + specs_header = SPECS_HEADER.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_driver_adapter.h", cmake) + self.assertIn("ccu/tilexr_ccu_driver_adapter.cpp", cmake) + self.assertIn("TileXRCcuDriverAdapter", header) + self.assertIn("TileXRCcuMsidTokenInfo", specs_header) + self.assertIn("TileXRCcuMsidTokenInfo msidToken", specs_header) + self.assertIn("basicInfo->msidToken.tokenId = raw.tokenId", source) + self.assertIn("basicInfo->msidToken.tokenValue = raw.tokenValue", source) + self.assertIn("basicInfo->msidToken.valid = raw.tokenValid != 0", source) + self.assertIn("TileXRCcuCustomChannelIn", header) + self.assertIn("TILEXR_CCU_U_OP_GET_BASIC_INFO", header) + self.assertIn("TILEXR_CCU_U_OP_GET_DIE_WORKING", header) + self.assertIn("TILEXR_CCU_U_OP_SET_MSID_TOKEN", header) + self.assertIn("TILEXR_CCU_U_OP_SET_INSTRUCTION", header) + self.assertIn("TILEXR_CCU_U_OP_SET_XN", header) + self.assertIn("TILEXR_CCU_U_OP_SET_CKE", header) + self.assertIn("TILEXR_CCU_U_OP_SET_PFE", header) + self.assertIn("TILEXR_CCU_U_OP_SET_CHANNEL", header) + self.assertIn("TILEXR_CCU_U_OP_SET_JETTY_CTX", header) + self.assertIn("TILEXR_CCU_XN_SLOT_BYTES", header) + self.assertIn("TileXRCcuPfeCtx", header) + self.assertIn("TileXRCcuLocalJettyCtxData", header) + self.assertIn("TileXRCcuChannelCtxDataV1", header) + self.assertIn("TileXRCcuCustomChannelFn", header) + self.assertIn("GetBasicInfo", header) + self.assertIn("GetDieEnabled", header) + self.assertIn("InstallInstructions", header) + self.assertIn("InstallMsidToken", header) + self.assertIn("InstallPfeCtx", header) + self.assertIn("InstallJettyCtx", header) + self.assertIn("InstallChannelCtxV1", header) + self.assertIn("ClearCkeRange", header) + self.assertIn("InstallXnRange", header) + + combined = header + "\n" + source + for needle in [ + "#include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 10; + } + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = true; + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "default install provider unexpectedly succeeded\n"; + return 2; + } + if (installReport.installAttempted || installReport.installSucceeded) { + std::cerr << "default install provider reported an attempted/succeeded install\n"; + return 3; + } + if (installReport.message.find("no public no-hcomm CCU install provider") == std::string::npos) { + std::cerr << "weak install provider diagnostic: " << installReport.message << "\n"; + return 4; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "default install provider filled install evidence\n"; + return 5; + } + if (evidence.missionSource.kind != TileXRCcuEvidenceKind::Missing || + evidence.repositorySource.kind != TileXRCcuEvidenceKind::Missing || + evidence.localXnSource.kind != TileXRCcuEvidenceKind::Missing || + evidence.remoteXnSource.kind != TileXRCcuEvidenceKind::Missing || + evidence.notifyCkeSource.kind != TileXRCcuEvidenceKind::Missing || + evidence.channelBindingSource.kind != TileXRCcuEvidenceKind::Missing) { + std::cerr << "default install provider did not mark sources missing\n"; + return 6; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "unsupported install evidence became submit-ready\n"; + return 7; + } + if (!submitTasks.empty() || providerReport.submitReady) { + std::cerr << "failed prepare leaked submit-ready state\n"; + return 8; + } + if (providerReport.message.find("mission") == std::string::npos || + providerReport.message.find("missing") == std::string::npos) { + std::cerr << "weak provider diagnostic: " << providerReport.message << "\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_request_must_match_bound_launch_scope(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int ExpectScopeFailure( + const TileXRCcuInstallRequest& request, + const char* diagnostic) + { + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport report; + const int ret = TileXRCcuInstallHardware(request, &evidence, &report); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "scope mismatch was not rejected: " << diagnostic << "\n"; + return 1; + } + if (report.message.find(diagnostic) == std::string::npos) { + std::cerr << "weak scope mismatch diagnostic: " << report.message << "\n"; + return 2; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed scope mismatch left install evidence\n"; + return 3; + } + return 0; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallRequest request; + request.package = &package; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + + TileXRCcuInstallRequest wrongDevice = request; + wrongDevice.deviceId = 4; + if (ExpectScopeFailure(wrongDevice, "device scope mismatch") != 0) { + return 3; + } + + TileXRCcuInstallRequest wrongRank = request; + wrongRank.rank = 0; + if (ExpectScopeFailure(wrongRank, "rank scope mismatch") != 0) { + return 4; + } + + TileXRCcuInstallRequest wrongProvider = request; + wrongProvider.provider = "other-public-install-provider"; + if (ExpectScopeFailure(wrongProvider, "provider scope mismatch") != 0) { + return 5; + } + + TileXRCcuInstallRequest missingProvider = request; + missingProvider.provider.clear(); + if (ExpectScopeFailure(missingProvider, "provider scope mismatch") != 0) { + return 6; + } + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport report; + if (TileXRCcuInstallHardware(request, &evidence, &report) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "matching scope should reach unsupported provider: " << report.message << "\n"; + return 7; + } + if (report.message.find("no public no-hcomm CCU install provider") == std::string::npos) { + std::cerr << "matching scope diagnostic was weak: " << report.message << "\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_online_install_provider_installs_repository_and_keeps_remaining_bits_missing(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + uint32_t observedOp = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint64_t observedResourceAddr = 0; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOp = in.op; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedResourceAddr = in.data.dataInfo.dataArray[0].insinfo.resourceAddr; + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "partial hardware install should stop at missing lower-layer resources: " + << installReport.message << "\n"; + return 5; + } + if (!installReport.installAttempted || !installReport.installSucceeded || + installReport.installAttemptReceiptId == 0 || + installReport.publicVerifiedInstallSurfaceCount != 2 || + installReport.missingInstallSurfaceCount != 4) { + std::cerr << "repository install report mismatch\n"; + return 6; + } + if (!installReport.repository.satisfied || !installReport.mission.satisfied || + installReport.localXn.satisfied || installReport.remoteXn.satisfied || + installReport.notifyCke.satisfied || installReport.channelBinding.satisfied) { + std::cerr << "unexpected install step evidence bits\n"; + return 7; + } + if (!evidence.repositoryInstalled || !evidence.missionInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "unexpected hardware evidence bits\n"; + return 8; + } + if (evidence.repositorySource.kind != TileXRCcuEvidenceKind::PublicVerified || + evidence.repositorySource.surface != TileXRCcuEvidenceSurface::PublicInstallProvider || + evidence.repositorySource.packageFingerprint != TileXRCcuComputeLaunchPackageFingerprint(package) || + evidence.repositorySource.deviceId != 3 || evidence.repositorySource.rank != 1 || + evidence.repositorySource.provider != "unit-test-public-install-provider" || + evidence.repositorySource.installAttemptReceiptId != installReport.installAttemptReceiptId) { + std::cerr << "repository evidence scope mismatch\n"; + return 9; + } + if (evidence.missionSource.kind != TileXRCcuEvidenceKind::PublicVerified || + evidence.missionSource.surface != TileXRCcuEvidenceSurface::PublicInstallProvider || + evidence.missionSource.installAttemptReceiptId != installReport.installAttemptReceiptId || + evidence.missionSource.detail.find("launch task descriptor") == std::string::npos || + evidence.missionSource.detail.find("mission key") == std::string::npos) { + std::cerr << "mission descriptor evidence mismatch\n"; + return 12; + } + if (state.observedOp != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.observedOffset != 489 || + state.observedDataLen != 156U * sizeof(TileXRCcuInstr) || + state.observedResourceAddr != receipt.deviceInstructionAddr || + receipt.instructionStartId != 489 || receipt.instructionCount != 156 || + !receipt.uploaded || !receipt.installed) { + std::cerr << "SET_INSTRUCTION request or receipt mismatch\n"; + return 10; + } + + TileXRCcuRepositoryReport releaseReport; + if (TileXRCcuReleaseRepositoryInstallReceipt(receipt, memoryOps, &state, &releaseReport) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "release failed: " << releaseReport.message << "\n"; + return 11; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_online_install_provider_installs_lower_layer_payloads_without_unlocking_submit_gate(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t die = 0; + uint32_t offset = 0; + uint32_t dataLen = 0; + uint32_t arraySize = 0; + uint8_t raw[160] = {0}; + uint32_t msId = 0; + uint32_t tokenId = 0; + uint32_t tokenValue = 0; + }; + + struct FakeState { + std::vector deviceBytes; + std::vector calls; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + ObservedCall call; + call.op = in.op; + call.die = in.data.dataInfo.udieIdx; + call.offset = in.offsetStartIdx; + call.dataLen = in.data.dataInfo.dataLen; + call.arraySize = in.data.dataInfo.dataArraySize; + call.msId = in.data.dataInfo.dataArray[0].baseinfo.msId; + call.tokenId = in.data.dataInfo.dataArray[0].baseinfo.tokenId; + call.tokenValue = in.data.dataInfo.dataArray[0].baseinfo.tokenValue; + std::memcpy(call.raw, in.data.dataInfo.dataArray, sizeof(call.raw)); + state->calls.push_back(call); + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 7; + for (uint32_t i = 0; i < TILEXR_CCU_PFE_CTX_BYTES; ++i) { + pfe.ctx.raw[i] = static_cast(0xa0 + i); + } + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(2); + for (uint32_t i = 0; i < TILEXR_CCU_LOCAL_JETTY_CTX_BYTES; ++i) { + jetty.ctxs[0].raw[i] = static_cast(0x10 + i); + jetty.ctxs[1].raw[i] = static_cast(0x50 + i); + } + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 11; + for (uint32_t i = 0; i < TILEXR_CCU_CHANNEL_CTX_V1_BYTES; ++i) { + channel.ctx.raw[i] = static_cast(0xc0 + i); + } + lowerLayer.channels.push_back(channel); + lowerLayer.xnClears.push_back({1, 32, 10}); + lowerLayer.ckeClears.push_back({1, 16, 10}); + + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "partial lower-layer install should not unlock submit gate: " + << installReport.message << "\n"; + return 5; + } + if (!installReport.installAttempted || !installReport.installSucceeded || + installReport.publicVerifiedInstallSurfaceCount != 4 || + installReport.missingInstallSurfaceCount != 2) { + std::cerr << "lower-layer install report mismatch\n"; + return 6; + } + if (!evidence.repositoryInstalled || !evidence.missionInstalled || !evidence.localXnInstalled || + !evidence.notifyCkeInstalled || evidence.channelBindingsInstalled || + evidence.remoteXnBound) { + std::cerr << "unexpected evidence bits after lower-layer install\n"; + return 7; + } + if (evidence.repositorySource.installAttemptReceiptId != installReport.installAttemptReceiptId || + evidence.missionSource.installAttemptReceiptId != installReport.installAttemptReceiptId || + evidence.localXnSource.installAttemptReceiptId != installReport.installAttemptReceiptId || + evidence.notifyCkeSource.installAttemptReceiptId != installReport.installAttemptReceiptId) { + std::cerr << "lower-layer evidence receipts mismatch\n"; + return 8; + } + if (evidence.missionSource.detail.find("launch task descriptor") == std::string::npos || + evidence.missionSource.detail.find("mission key") == std::string::npos || + evidence.localXnSource.detail.find("SET_XN") == std::string::npos || + evidence.notifyCkeSource.detail.find("SET_CKE") == std::string::npos || + evidence.channelBindingSource.kind != TileXRCcuEvidenceKind::Missing || + evidence.channelBindingSource.detail.find("channel binding install evidence is missing") == + std::string::npos || + installReport.channelBinding.satisfied || + installReport.channelBinding.message.find("channel binding endpoint route provenance was not verified") == + std::string::npos) { + std::cerr << "lower-layer evidence detail is weak\n"; + return 9; + } + + if (state.calls.size() != 9 || + state.calls[0].op != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.calls[1].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.calls[2].op != TILEXR_CCU_U_OP_SET_PFE || + state.calls[3].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.calls[4].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[5].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[6].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[7].op != TILEXR_CCU_U_OP_SET_CKE || + state.calls[8].op != TILEXR_CCU_U_OP_SET_CKE) { + std::cerr << "unexpected lower-layer call sequence\n"; + return 10; + } + if (state.calls[1].msId != 0x45 || state.calls[1].tokenId != 0x1234 || + state.calls[1].tokenValue != 0x5678) { + std::cerr << "MSID token request mismatch\n"; + return 11; + } + if (state.calls[2].offset != 7 || state.calls[2].dataLen != TILEXR_CCU_PFE_CTX_BYTES || + std::memcmp(state.calls[2].raw, pfe.ctx.raw, TILEXR_CCU_PFE_CTX_BYTES) != 0) { + std::cerr << "PFE request mismatch\n"; + return 12; + } + if (state.calls[3].offset != 9 || + state.calls[3].dataLen != 2 * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES || + std::memcmp(state.calls[3].raw, jetty.ctxs[0].raw, TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0 || + std::memcmp( + state.calls[3].raw + TILEXR_CCU_DATA_ARRAY_SLOT_BYTES, + jetty.ctxs[1].raw, + TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0) { + std::cerr << "Jetty request mismatch\n"; + return 13; + } + if (state.calls[4].offset != 11 || + state.calls[4].dataLen != TILEXR_CCU_CHANNEL_CTX_V1_BYTES || + std::memcmp(state.calls[4].raw, channel.ctx.raw, TILEXR_CCU_CHANNEL_CTX_V1_BYTES) != 0) { + std::cerr << "Channel request mismatch\n"; + return 14; + } + if (state.calls[5].offset != 32 || state.calls[5].arraySize != 8 || + state.calls[6].offset != 40 || state.calls[6].arraySize != 2) { + std::cerr << "XN batching mismatch\n"; + return 15; + } + if (state.calls[7].offset != 16 || state.calls[7].arraySize != 8 || + state.calls[8].offset != 24 || state.calls[8].arraySize != 2) { + std::cerr << "CKE batching mismatch\n"; + return 16; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "partial lower-layer evidence became submit-ready\n"; + return 17; + } + if (providerReport.message.find("remote XN") == std::string::npos || + providerReport.message.find("missing") == std::string::npos) { + std::cerr << "submit rejection diagnostic is weak: " << providerReport.message << "\n"; + return 18; + } + + TileXRCcuRepositoryReport releaseReport; + if (TileXRCcuReleaseRepositoryInstallReceipt(receipt, memoryOps, &state, &releaseReport) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "release failed: " << releaseReport.message << "\n"; + return 19; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_online_install_provider_promotes_remote_xn_when_peer_exchange_matches_sync_resources(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t offset = 0; + }; + + struct FakeState { + std::vector deviceBytes; + std::vector calls; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void* userData) + { + auto* state = static_cast(userData); + state->freed = true; + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls.push_back({in.op, in.offsetStartIdx}); + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 3}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 365, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 366, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeLowerLayer(bool staleRemoteXn) + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + for (uint32_t i = 0; i < 3; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2 + i; + channel.ctx.raw[0] = 0xc0 + i; + lowerLayer.channels.push_back(channel); + } + + lowerLayer.xnClears.push_back({1, 1961, 3}); + lowerLayer.ckeClears.push_back({1, 332, 3}); + + lowerLayer.remoteXnBindings.push_back( + {1, 2, 1961, staleRemoteXn ? 2369 : 2361, 364, 0, true, 0, true, true, true}); + lowerLayer.remoteXnBindings.push_back({1, 3, 1962, 2362, 365, 0, true, 0, true, true, true}); + lowerLayer.remoteXnBindings.push_back({1, 4, 1963, 2364, 366, 0, true, 0, true, true, true}); + return lowerLayer; + } + + int RunInstall(const TileXRCcuLaunchPackage& package, + const TileXRCcuInstallManifest& manifest, + const TileXRCcuLowerLayerInstallPlan& lowerLayer, + FakeState* state, + TileXRCcuHardwareInstallEvidence* evidence, + TileXRCcuInstallProviderReport* installReport) + { + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 100; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + const int ret = TileXRCcuInstallHardware(request, evidence, installReport); + TileXRCcuRepositoryReport releaseReport; + if (receipt.deviceInstructionAddr != 0 && + TileXRCcuReleaseRepositoryInstallReceipt(receipt, memoryOps, state, &releaseReport) != + TILEXR_SUCCESS) { + std::cerr << "release failed: " << releaseReport.message << "\n"; + return 101; + } + return ret; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState staleState; + TileXRCcuHardwareInstallEvidence staleEvidence; + TileXRCcuInstallProviderReport staleReport; + TileXRCcuLowerLayerInstallPlan staleLowerLayer = MakeLowerLayer(true); + if (RunInstall(package, manifest, staleLowerLayer, &staleState, &staleEvidence, &staleReport) != + TILEXR_ERROR_NOT_FOUND) { + std::cerr << "stale remote XN proof should not complete install\n"; + return 4; + } + if (staleEvidence.remoteXnBound || staleReport.remoteXn.satisfied || + staleReport.publicVerifiedInstallSurfaceCount != 5 || + staleReport.missingInstallSurfaceCount != 1 || + staleReport.remoteXn.message.find("remote XN peer exchange proof") == std::string::npos) { + std::cerr << "stale remote XN proof was accepted or weakly diagnosed: " + << staleReport.remoteXn.message << "\n"; + return 5; + } + + FakeState state; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeLowerLayer(false); + if (RunInstall(package, manifest, lowerLayer, &state, &evidence, &installReport) != + TILEXR_SUCCESS) { + std::cerr << "complete remote XN proof should complete install: " + << installReport.message << "\n"; + return 6; + } + if (!installReport.installAttempted || !installReport.installSucceeded || + installReport.publicVerifiedInstallSurfaceCount != 6 || + installReport.missingInstallSurfaceCount != 0) { + std::cerr << "complete install report mismatch\n"; + return 7; + } + if (!evidence.repositoryInstalled || !evidence.missionInstalled || + !evidence.localXnInstalled || !evidence.remoteXnBound || + !evidence.notifyCkeInstalled || !evidence.channelBindingsInstalled) { + std::cerr << "complete install evidence mismatch\n"; + return 8; + } + if (evidence.remoteXnSource.source.find("ValidateRemoteXnExchangeBindingProof") == + std::string::npos || + evidence.remoteXnSource.detail.find("peer exchange") == std::string::npos || + evidence.remoteXnSource.detail.find("verified endpoint route channel contexts") == + std::string::npos || + evidence.remoteXnSource.detail.find("channel resource owner") == std::string::npos || + evidence.remoteXnSource.detail.find("transport resource exchange") == std::string::npos || + !evidence.remoteXnSource.endpointRouteVerified) { + std::cerr << "remote XN evidence detail is weak\n"; + return 9; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_SUCCESS) { + std::cerr << "submit preparation failed: " << providerReport.message << "\n"; + return 10; + } + if (!providerReport.submitReady || submitTasks.size() != package.tasks.size()) { + std::cerr << "submit readiness mismatch\n"; + return 11; + } + if (state.calls.size() != 9 || + state.calls[0].op != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.calls[4].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[5].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[6].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[7].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[8].op != TILEXR_CCU_U_OP_SET_CKE) { + std::cerr << "unexpected complete install call sequence\n"; + return 12; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_lower_layer_first_repository_failure_reports_installed_preconditions(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + }; + + struct FakeState { + std::vector deviceBytes; + std::vector calls; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void* userData) + { + auto* state = static_cast(userData); + state->freed = true; + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls.push_back({in.op}); + out->opRet = in.op == TILEXR_CCU_U_OP_SET_INSTRUCTION ? 0x51 : 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 365, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 366, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + for (uint32_t i = 0; i < 3; ++i) { + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2 + i; + channel.ctx.raw[0] = static_cast(0xc0 + i); + lowerLayer.channels.push_back(channel); + } + + lowerLayer.xnClears.push_back({1, 1961, 3}); + lowerLayer.ckeClears.push_back({1, 332, 3}); + lowerLayer.remoteXnBindings.push_back({1, 2, 1961, 2361, 364, 0, true, 0, true}); + lowerLayer.remoteXnBindings.push_back({1, 3, 1962, 2362, 365, 0, true, 0, true}); + lowerLayer.remoteXnBindings.push_back({1, 4, 1963, 2364, 366, 0, true, 0, true}); + return lowerLayer; + } + + bool Contains(const std::string& text, const char* needle) + { + return text.find(needle) != std::string::npos; + } + + bool Contains(const std::string& text, const std::string& needle) + { + return text.find(needle) != std::string::npos; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeLowerLayer(); + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.installOrder = TileXRCcuInstallOrder::InstallLowerLayerFirst; + request.lowerLayerPlan = &lowerLayer; + + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_MKIRT) { + std::cerr << "repository failure should propagate MKIRT: " << installReport.message << "\n"; + return 5; + } + if (!installReport.installAttempted || installReport.installSucceeded || + installReport.publicVerifiedInstallSurfaceCount != 0 || + installReport.missingInstallSurfaceCount != 6) { + std::cerr << "repository failure report counts mismatch\n"; + return 6; + } + if (evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || + evidence.channelBindingsInstalled || evidence.missionInstalled) { + std::cerr << "failed repository install leaked hardware evidence\n"; + return 7; + } + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "failed repository install released submit gate\n"; + return 8; + } + if (providerReport.submitReady || !submitTasks.empty()) { + std::cerr << "failed repository install leaked submit-ready state\n"; + return 9; + } + if (state.calls.size() < 9 || + state.calls[0].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.calls[1].op != TILEXR_CCU_U_OP_SET_PFE || + state.calls[2].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.calls[3].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[4].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[5].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.calls[6].op != TILEXR_CCU_U_OP_SET_XN || + state.calls[7].op != TILEXR_CCU_U_OP_SET_CKE || + state.calls[8].op != TILEXR_CCU_U_OP_SET_INSTRUCTION) { + std::cerr << "lower-layer-first call sequence mismatch\n"; + return 10; + } + for (size_t i = 9; i < state.calls.size(); ++i) { + if (state.calls[i].op != TILEXR_CCU_U_OP_GET_INSTRUCTION) { + std::cerr << "repository failure diagnostic readback sequence mismatch\n"; + return 10; + } + } + if (!state.freed || receipt.deviceInstructionPtr != nullptr || receipt.installed) { + std::cerr << "failed repository install did not release uploaded image\n"; + return 11; + } + const std::string summaryNeedles[] = { + "lowerLayerPreconditions{", + "msidTokenCount=1", + "pfeCount=1", + "jettyCount=1", + "channelCount=3", + "xnClearCount=1", + "ckeClearCount=1", + "localXnInstalled=1", + "notifyCkeInstalled=1", + "channelBindingInstalled=1", + "msidToken0{dieId=1 msId=69 tokenId=0x1234 tokenValue=0x5678}", + "pfe0{dieId=1 offset=2}", + "jetty0{dieId=1 startJettyCtxId=9 ctxCount=1}", + "channel0{dieId=1 channelId=2}", + "xnClear0{dieId=1 startXnId=1961 count=3}", + "ckeClear0{dieId=1 startCkeId=332 count=3}", + "remoteXn0{dieId=1 channelId=2 localXn=1961 remoteXn=2361 notifyCke=364", + }; + for (const auto& needle : summaryNeedles) { + if (!Contains(installReport.message, needle)) { + std::cerr << "missing lower-layer precondition summary: " << needle + << " in " << installReport.message << "\n"; + return 12; + } + } + if (!Contains(installReport.message, "CCU custom channel operation failed op=") || + !Contains(installReport.message, "opRet=81")) { + std::cerr << "repository failure context is missing: " << installReport.message << "\n"; + return 13; + } + if (!Contains(installReport.localXn.message, "lowerLayerPreconditions{") || + !Contains(installReport.remoteXn.message, "lowerLayerPreconditions{") || + !Contains(installReport.notifyCke.message, "lowerLayerPreconditions{") || + !Contains(installReport.channelBinding.message, "lowerLayerPreconditions{")) { + std::cerr << "lower-layer step messages lost precondition summary\n"; + return 14; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_remote_xn_proof_checks_remote_notify_and_local_wait_cke(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel(uint32_t, const TileXRCcuCustomChannelIn&, TileXRCcuCustomChannelOut* out, void*) + { + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 4}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 3; + resource.localWaitCke = 332; + resource.localWaitMask = 1; + resource.remoteNotifyMask = 1; + plan.syncResources.push_back(resource); + + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeLowerLayer(uint16_t remoteNotifyCke, uint16_t localWaitCke) + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2; + channel.ctx.raw[0] = 0xc0; + lowerLayer.channels.push_back(channel); + + lowerLayer.xnClears.push_back({1, 1961, 1}); + lowerLayer.ckeClears.push_back({1, 332, 4}); + + TileXRCcuRemoteXnBindingProof proof; + proof.dieId = 1; + proof.channelId = 2; + proof.localXn = 1961; + proof.remoteXn = 2361; + proof.notifyCke = remoteNotifyCke; + proof.localWaitCke = localWaitCke; + proof.peerRank = 0; + proof.peerExchangeObserved = true; + proof.endpointRouteVerified = true; + proof.channelResourceOwnerVerified = true; + proof.transportResourceExchangeVerified = true; + lowerLayer.remoteXnBindings.push_back(proof); + return lowerLayer; + } + + int RunInstall( + const TileXRCcuLaunchPackage& package, + const TileXRCcuInstallManifest& manifest, + const TileXRCcuLowerLayerInstallPlan& lowerLayer, + TileXRCcuInstallProviderReport* report) + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + return TILEXR_ERROR_INTERNAL; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + TileXRCcuHardwareInstallEvidence evidence; + return TileXRCcuInstallHardware(request, &evidence, report); + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + TileXRCcuInstallProviderReport staleLocalWait; + if (RunInstall(package, manifest, MakeLowerLayer(364, 333), &staleLocalWait) != + TILEXR_ERROR_NOT_FOUND) { + std::cerr << "stale local wait CKE proof was accepted\n"; + return 4; + } + if (staleLocalWait.remoteXn.satisfied || + staleLocalWait.remoteXn.message.find("local wait CKE") == std::string::npos) { + std::cerr << "local wait CKE diagnostic was weak: " + << staleLocalWait.remoteXn.message << "\n"; + return 5; + } + + TileXRCcuInstallProviderReport staleRemoteNotify; + if (RunInstall(package, manifest, MakeLowerLayer(365, 332), &staleRemoteNotify) != + TILEXR_ERROR_NOT_FOUND) { + std::cerr << "stale remote notify CKE proof was accepted\n"; + return 6; + } + if (staleRemoteNotify.remoteXn.satisfied || + staleRemoteNotify.remoteXn.message.find("remote notify CKE") == std::string::npos) { + std::cerr << "remote notify CKE diagnostic was weak: " + << staleRemoteNotify.remoteXn.message << "\n"; + return 7; + } + + TileXRCcuInstallProviderReport ok; + if (RunInstall(package, manifest, MakeLowerLayer(364, 332), &ok) != TILEXR_SUCCESS || + !ok.remoteXn.satisfied) { + std::cerr << "complete dual CKE proof was rejected: " << ok.message << "\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_online_install_provider_rejects_stale_mission_launch_descriptor_proof(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + uint32_t observedOp = 0; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOp = in.op; + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + + package.tasks[0].key ^= 0x1U; + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "stale mission descriptor should leave install incomplete: " + << installReport.message << "\n"; + return 5; + } + if (!installReport.repository.satisfied || installReport.mission.satisfied || + !evidence.repositoryInstalled || evidence.missionInstalled) { + std::cerr << "stale mission descriptor was promoted\n"; + return 6; + } + if (installReport.publicVerifiedInstallSurfaceCount != 1 || + installReport.missingInstallSurfaceCount != 5) { + std::cerr << "stale descriptor install counts mismatch\n"; + return 7; + } + if (evidence.missionSource.kind != TileXRCcuEvidenceKind::Missing || + installReport.mission.message.find("launch task descriptor") == std::string::npos || + installReport.mission.message.find("mission key") == std::string::npos) { + std::cerr << "stale descriptor diagnostic is weak: " + << installReport.mission.message << "\n"; + return 8; + } + if (state.observedOp != TILEXR_CCU_U_OP_SET_INSTRUCTION) { + std::cerr << "repository install did not run before descriptor proof\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_evidence_adapter_keeps_offline_candidates_not_submit_ready(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuInstallStepEvidence Candidate(const char* source, const char* detail) + { + TileXRCcuInstallStepEvidence step; + step.satisfied = true; + step.source.kind = TileXRCcuEvidenceKind::PublicCandidate; + step.source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + step.source.source = source; + step.source.detail = detail; + step.message = detail; + return step; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 8; + } + + TileXRCcuInstallProviderReport installReport; + installReport.offlineOnly = true; + installReport.mission = Candidate("scan:mission", "public-looking mission candidate"); + installReport.repository = Candidate("scan:repository", "public-looking repository candidate"); + installReport.localXn = Candidate("scan:local-xn", "public-looking local XN candidate"); + installReport.remoteXn = Candidate("scan:remote-xn", "public-looking remote XN candidate"); + installReport.notifyCke = Candidate("scan:notify-cke", "public-looking notify CKE candidate"); + installReport.channelBinding = Candidate("scan:channel", "public-looking channel candidate"); + + TileXRCcuHardwareInstallEvidence evidence; + if (TileXRCcuBuildInstallEvidence(installReport, &evidence) != TILEXR_SUCCESS) { + std::cerr << "adapter rejected candidate report\n"; + return 2; + } + if (!evidence.missionInstalled || !evidence.repositoryInstalled || !evidence.localXnInstalled || + !evidence.remoteXnBound || !evidence.notifyCkeInstalled || !evidence.channelBindingsInstalled) { + std::cerr << "adapter dropped candidate evidence unexpectedly\n"; + return 3; + } + if (evidence.missionSource.kind != TileXRCcuEvidenceKind::PublicCandidate || + evidence.channelBindingSource.kind != TileXRCcuEvidenceKind::PublicCandidate) { + std::cerr << "adapter upgraded/downgraded candidate evidence unexpectedly\n"; + return 4; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "offline public candidates became submit-ready\n"; + return 5; + } + if (!submitTasks.empty() || providerReport.submitReady) { + std::cerr << "failed candidate prepare leaked submit-ready state\n"; + return 6; + } + if (providerReport.message.find("candidate") == std::string::npos || + providerReport.message.find("public install provider") == std::string::npos) { + std::cerr << "weak candidate diagnostic: " << providerReport.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_evidence_adapter_does_not_accept_offline_public_verified_claims(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuInstallStepEvidence ForgedVerified(const char* source, const char* detail) + { + TileXRCcuInstallStepEvidence step; + step.satisfied = true; + step.source.kind = TileXRCcuEvidenceKind::PublicVerified; + step.source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + step.source.source = source; + step.source.detail = detail; + step.message = detail; + return step; + } + + int main() + { + TileXRCcuInstallProviderReport installReport; + installReport.offlineOnly = true; + installReport.mission = ForgedVerified("forged:mission", "forged mission/key evidence"); + installReport.repository = ForgedVerified("forged:repository", "forged repository evidence"); + installReport.localXn = ForgedVerified("forged:local-xn", "forged local XN evidence"); + installReport.remoteXn = ForgedVerified("forged:remote-xn", "forged remote XN evidence"); + installReport.notifyCke = ForgedVerified("forged:notify-cke", "forged notify CKE evidence"); + installReport.channelBinding = ForgedVerified("forged:channel", "forged channel binding evidence"); + + TileXRCcuHardwareInstallEvidence evidence; + if (TileXRCcuBuildInstallEvidence(installReport, &evidence) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "offline public verified install claims were accepted\n"; + return 1; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed offline verified adapter left install evidence\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_evidence_adapter_requires_consistent_online_receipt(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuInstallStepEvidence VerifiedStep( + const char* source, + const char* detail, + uint64_t receipt) + { + TileXRCcuInstallStepEvidence step; + step.satisfied = true; + step.source.kind = TileXRCcuEvidenceKind::PublicVerified; + step.source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + step.source.packageFingerprint = 0xfeed001ULL; + step.source.deviceId = 3; + step.source.rank = 1; + step.source.provider = "unit-test-public-install-provider"; + step.source.source = source; + step.source.detail = detail; + step.source.installAttemptReceiptId = receipt; + step.message = detail; + return step; + } + + TileXRCcuInstallProviderReport VerifiedReport(uint64_t receipt) + { + TileXRCcuInstallProviderReport report; + report.offlineOnly = false; + report.installAttempted = true; + report.installSucceeded = true; + report.installAttemptReceiptId = receipt; + report.mission = VerifiedStep("provider:mission", "mission/key installed", receipt); + report.repository = VerifiedStep("provider:repository", "repository installed", receipt); + report.localXn = VerifiedStep("provider:local-xn", "local XN installed", receipt); + report.remoteXn = VerifiedStep("provider:remote-xn", "remote XN bound", receipt); + report.notifyCke = VerifiedStep("provider:notify-cke", "notify CKE installed", receipt); + report.channelBinding = VerifiedStep("provider:channel", "channel binding installed", receipt); + return report; + } + + int main() + { + TileXRCcuHardwareInstallEvidence evidence; + + TileXRCcuInstallProviderReport missingReceipt = VerifiedReport(0); + if (TileXRCcuBuildInstallEvidence(missingReceipt, &evidence) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "online public verified report with missing receipt was accepted\n"; + return 1; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed missing receipt adapter left install evidence\n"; + return 2; + } + + TileXRCcuInstallProviderReport mixedReceipt = VerifiedReport(0xabc001ULL); + mixedReceipt.remoteXn.source.installAttemptReceiptId = 0xabc002ULL; + if (TileXRCcuBuildInstallEvidence(mixedReceipt, &evidence) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "online public verified report with mixed receipts was accepted\n"; + return 3; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed mixed receipt adapter left install evidence\n"; + return 4; + } + + TileXRCcuInstallProviderReport notAttempted = VerifiedReport(0xabc001ULL); + notAttempted.installAttempted = false; + if (TileXRCcuBuildInstallEvidence(notAttempted, &evidence) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "online public verified report without attempted install was accepted\n"; + return 5; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed not-attempted adapter left install evidence\n"; + return 6; + } + + TileXRCcuInstallProviderReport notSucceeded = VerifiedReport(0xabc001ULL); + notSucceeded.installSucceeded = false; + if (TileXRCcuBuildInstallEvidence(notSucceeded, &evidence) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "online public verified report without successful install was accepted\n"; + return 7; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed not-succeeded adapter left install evidence\n"; + return 8; + } + + TileXRCcuInstallProviderReport matchingReceipt = VerifiedReport(0xabc001ULL); + if (TileXRCcuBuildInstallEvidence(matchingReceipt, &evidence) != TILEXR_SUCCESS) { + std::cerr << "matching receipt report was rejected\n"; + return 9; + } + if (evidence.missionSource.installAttemptReceiptId != 0xabc001ULL || + evidence.repositorySource.installAttemptReceiptId != 0xabc001ULL || + evidence.localXnSource.installAttemptReceiptId != 0xabc001ULL || + evidence.remoteXnSource.installAttemptReceiptId != 0xabc001ULL || + evidence.notifyCkeSource.installAttemptReceiptId != 0xabc001ULL || + evidence.channelBindingSource.installAttemptReceiptId != 0xabc001ULL) { + std::cerr << "adapter did not preserve matching receipt ids\n"; + return 10; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_manifest_from_launch_package_lists_all_required_surfaces(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + const TileXRCcuInstallRequirement* FindRequirement( + const TileXRCcuInstallManifest& manifest, + TileXRCcuInstallRequirementKind kind) + { + for (const auto& requirement : manifest.requirements) { + if (requirement.kind == kind) { + return &requirement; + } + } + return nullptr; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + const uint64_t fingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (manifest.deviceId != 3 || manifest.rank != 1 || + manifest.provider != "unit-test-public-install-provider" || + manifest.packageFingerprint != fingerprint || + !manifest.requiresHardwareInstall || + !manifest.installAttemptReceiptRequired || + manifest.requiredEvidenceKind != TileXRCcuEvidenceKind::PublicVerified || + manifest.requiredEvidenceSurface != TileXRCcuEvidenceSurface::PublicInstallProvider) { + std::cerr << "manifest did not preserve scope/fingerprint/evidence contract\n"; + return 4; + } + if (manifest.requirements.size() != 6 || manifestReport.requirementCount != 6 || + manifestReport.message != "ok") { + std::cerr << "manifest did not list the six required install surfaces\n"; + return 5; + } + + const TileXRCcuInstallRequirement* mission = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::MissionKey); + const TileXRCcuInstallRequirement* repository = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::RepositoryImage); + const TileXRCcuInstallRequirement* localXn = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::LocalXn); + const TileXRCcuInstallRequirement* remoteXn = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::RemoteXnBinding); + const TileXRCcuInstallRequirement* notifyCke = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::NotifyCke); + const TileXRCcuInstallRequirement* channel = + FindRequirement(manifest, TileXRCcuInstallRequirementKind::ChannelBinding); + if (mission == nullptr || repository == nullptr || localXn == nullptr || + remoteXn == nullptr || notifyCke == nullptr || channel == nullptr) { + std::cerr << "manifest missed one or more required install kinds\n"; + return 6; + } + if (mission->missionId != 6 || mission->missionKey != 0x059b0f03U || + mission->resourceStartId != 6 || mission->resourceCount != 1) { + std::cerr << "manifest mission/key requirement is incomplete\n"; + return 7; + } + if (repository->repositoryStartId != 475 || repository->repositoryCount != 170 || + repository->missionStartId != 489 || repository->missionCount != 156 || + repository->instructionCount != 170) { + std::cerr << "manifest repository requirement is incomplete\n"; + return 8; + } + if (localXn->resourceStartId != 1961 || localXn->resourceCount != 62) { + std::cerr << "manifest local XN requirement is incomplete\n"; + return 9; + } + if (remoteXn->syncResourceCount != 3 || notifyCke->syncResourceCount != 3 || + channel->syncResourceCount != 3 || channel->bindingCount != 9) { + std::cerr << "manifest sync resource requirements are incomplete\n"; + return 10; + } + for (const auto& requirement : manifest.requirements) { + if (requirement.packageFingerprint != fingerprint || + requirement.label.empty() || + requirement.detail.empty()) { + std::cerr << "manifest requirement missed audit metadata\n"; + return 11; + } + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_manifest_rejects_unbound_or_stale_scope_without_partial_output(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int ExpectManifestFailure( + const TileXRCcuLaunchPackage& package, + const char* diagnostic) + { + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport report; + const int ret = TileXRCcuBuildInstallManifest(package, &manifest, &report); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "manifest build unexpectedly succeeded for " << diagnostic << "\n"; + return 1; + } + if (report.message.find(diagnostic) == std::string::npos) { + std::cerr << "weak manifest diagnostic: " << report.message << "\n"; + return 2; + } + if (!manifest.requirements.empty() || report.requirementCount != 0) { + std::cerr << "failed manifest build left partial requirements\n"; + return 3; + } + return 0; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (ExpectManifestFailure(package, "launch install scope is not bound") != 0) { + return 2; + } + + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 3; + } + package.tasks[0].key ^= 0x1U; + if (ExpectManifestFailure(package, "launch install scope is stale") != 0) { + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_manifest_contract_does_not_make_default_provider_submit_ready(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "default install provider accepted manifest as installed evidence\n"; + return 4; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "manifest leaked into install evidence\n"; + return 5; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "manifest-only default provider became submit-ready\n"; + return 6; + } + if (!submitTasks.empty() || providerReport.submitReady || + providerReport.missingEvidenceCount == 0) { + std::cerr << "failed manifest-only prepare leaked submit-ready state\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_manifest_default_provider_reports_missing_required_surfaces(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + + if (TileXRCcuInstallHardware(request, &evidence, &installReport) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "default provider did not stop at unsupported install path\n"; + return 4; + } + if (installReport.requiredInstallSurfaceCount != 6 || + installReport.publicVerifiedInstallSurfaceCount != 0 || + installReport.missingInstallSurfaceCount != 6) { + std::cerr << "manifest requirement counts were not reported: required=" + << installReport.requiredInstallSurfaceCount + << " verified=" << installReport.publicVerifiedInstallSurfaceCount + << " missing=" << installReport.missingInstallSurfaceCount << "\n"; + return 5; + } + if (installReport.message.find("no public no-hcomm CCU install provider") == std::string::npos) { + std::cerr << "weak unsupported provider diagnostic: " << installReport.message << "\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_request_rejects_mismatched_manifest_scope(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int ExpectManifestFailure( + const TileXRCcuInstallRequest& request, + const char* diagnostic) + { + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport report; + const int ret = TileXRCcuInstallHardware(request, &evidence, &report); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "manifest mismatch was not rejected: " << diagnostic << "\n"; + return 1; + } + if (report.message.find(diagnostic) == std::string::npos) { + std::cerr << "weak manifest mismatch diagnostic: " << report.message << "\n"; + return 2; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed manifest mismatch left install evidence\n"; + return 3; + } + return 0; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + + TileXRCcuInstallManifest wrongDevice = manifest; + wrongDevice.deviceId = 4; + request.manifest = &wrongDevice; + if (ExpectManifestFailure(request, "install manifest device mismatch") != 0) { + return 4; + } + + TileXRCcuInstallManifest wrongFingerprint = manifest; + wrongFingerprint.packageFingerprint ^= 0x1ULL; + request.manifest = &wrongFingerprint; + if (ExpectManifestFailure(request, "install manifest fingerprint mismatch") != 0) { + return 5; + } + + TileXRCcuInstallManifest wrongRequirementKind = manifest; + wrongRequirementKind.requirements[5].kind = wrongRequirementKind.requirements[0].kind; + request.manifest = &wrongRequirementKind; + if (ExpectManifestFailure(request, "install manifest requirement kind mismatch") != 0) { + return 6; + } + + TileXRCcuInstallManifest wrongHardwareRequirement = manifest; + wrongHardwareRequirement.requiresHardwareInstall = false; + request.manifest = &wrongHardwareRequirement; + if (ExpectManifestFailure(request, "install manifest hardware requirement mismatch") != 0) { + return 7; + } + + TileXRCcuInstallManifest wrongMission = manifest; + wrongMission.requirements[0].missionKey ^= 0x1U; + request.manifest = &wrongMission; + if (ExpectManifestFailure(request, "install manifest mission requirement mismatch") != 0) { + return 8; + } + + TileXRCcuInstallManifest wrongChannel = manifest; + wrongChannel.requirements[5].bindingCount = 1; + request.manifest = &wrongChannel; + if (ExpectManifestFailure(request, "install manifest channel requirement mismatch") != 0) { + return 9; + } + + request.manifest = &manifest; + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport report; + if (TileXRCcuInstallHardware(request, &evidence, &report) != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "matching manifest should reach unsupported provider: " << report.message << "\n"; + return 10; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_evidence_adapter_rejects_public_verified_with_bad_surface_or_scope(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuInstallStepEvidence VerifiedStep( + const char* source, + const char* detail, + uint64_t receipt) + { + TileXRCcuInstallStepEvidence step; + step.satisfied = true; + step.source.kind = TileXRCcuEvidenceKind::PublicVerified; + step.source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + step.source.packageFingerprint = 0xfeed001ULL; + step.source.deviceId = 3; + step.source.rank = 1; + step.source.provider = "unit-test-public-install-provider"; + step.source.installAttemptReceiptId = receipt; + step.source.source = source; + step.source.detail = detail; + step.message = detail; + return step; + } + + TileXRCcuInstallProviderReport VerifiedReport(uint64_t receipt) + { + TileXRCcuInstallProviderReport report; + report.offlineOnly = false; + report.installAttempted = true; + report.installSucceeded = true; + report.installAttemptReceiptId = receipt; + report.mission = VerifiedStep("provider:mission", "mission/key installed", receipt); + report.repository = VerifiedStep("provider:repository", "repository installed", receipt); + report.localXn = VerifiedStep("provider:local-xn", "local XN installed", receipt); + report.remoteXn = VerifiedStep("provider:remote-xn", "remote XN bound", receipt); + report.notifyCke = VerifiedStep("provider:notify-cke", "notify CKE installed", receipt); + report.channelBinding = VerifiedStep("provider:channel", "channel binding installed", receipt); + return report; + } + + int ExpectAdapterFailure(const TileXRCcuInstallProviderReport& report, const char* diagnostic) + { + TileXRCcuHardwareInstallEvidence evidence; + const int ret = TileXRCcuBuildInstallEvidence(report, &evidence); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid public verified evidence was accepted: " << diagnostic << "\n"; + return 1; + } + if (evidence.missionInstalled || evidence.repositoryInstalled || evidence.localXnInstalled || + evidence.remoteXnBound || evidence.notifyCkeInstalled || evidence.channelBindingsInstalled) { + std::cerr << "failed adapter left invalid public verified evidence\n"; + return 2; + } + return 0; + } + + int main() + { + TileXRCcuInstallProviderReport wrongSurface = VerifiedReport(0xabc001ULL); + wrongSurface.mission.source.surface = TileXRCcuEvidenceSurface::LowerLayerResourceHelper; + if (ExpectAdapterFailure(wrongSurface, "surface mismatch") != 0) { + return 1; + } + + TileXRCcuInstallProviderReport missingScope = VerifiedReport(0xabc001ULL); + missingScope.repository.source.packageFingerprint = 0; + if (ExpectAdapterFailure(missingScope, "missing fingerprint") != 0) { + return 2; + } + + TileXRCcuInstallProviderReport missingProvider = VerifiedReport(0xabc001ULL); + missingProvider.localXn.source.provider.clear(); + if (ExpectAdapterFailure(missingProvider, "missing provider") != 0) { + return 3; + } + + TileXRCcuInstallProviderReport missingDetail = VerifiedReport(0xabc001ULL); + missingDetail.remoteXn.source.detail.clear(); + if (ExpectAdapterFailure(missingDetail, "missing source/detail") != 0) { + return 4; + } + + TileXRCcuHardwareInstallEvidence evidence; + if (TileXRCcuBuildInstallEvidence(VerifiedReport(0xabc001ULL), &evidence) != TILEXR_SUCCESS) { + std::cerr << "valid public verified evidence was rejected\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_peer_exchange_without_endpoint_route_provenance_does_not_submit(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 3}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 1}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeSyntheticLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2; + channel.ctx.raw[0] = 0xc0; + lowerLayer.channels.push_back(channel); + + lowerLayer.xnClears.push_back({1, 1961, 1}); + lowerLayer.ckeClears.push_back({1, 332, 1}); + + TileXRCcuRemoteXnBindingProof proof; + proof.dieId = 1; + proof.channelId = 2; + proof.localXn = 1961; + proof.remoteXn = 2361; + proof.notifyCke = 364; + proof.peerRank = 0; + proof.peerExchangeObserved = true; + lowerLayer.remoteXnBindings.push_back(proof); + return lowerLayer; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeSyntheticLowerLayer(); + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + const int ret = TileXRCcuInstallHardware(request, &evidence, &installReport); + if (ret != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "synthetic endpoint route unexpectedly completed install: " + << installReport.message << "\n"; + return 5; + } + if (evidence.remoteXnBound || evidence.channelBindingsInstalled || + installReport.remoteXn.satisfied || installReport.channelBinding.satisfied || + installReport.publicVerifiedInstallSurfaceCount != 4 || + installReport.missingInstallSurfaceCount != 2) { + std::cerr << "synthetic endpoint route produced submit evidence\n"; + return 6; + } + if (installReport.remoteXn.message.find("endpoint route") == std::string::npos || + installReport.channelBinding.message.find("endpoint route") == std::string::npos) { + std::cerr << "weak synthetic endpoint diagnostic remote=\"" + << installReport.remoteXn.message << "\" channel=\"" + << installReport.channelBinding.message << "\"\n"; + return 7; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "synthetic endpoint route became submit-ready\n"; + return 8; + } + if (!submitTasks.empty() || providerReport.submitReady) { + std::cerr << "submit-ready state leaked from synthetic endpoint route\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_peer_exchange_with_endpoint_route_but_without_channel_resource_binding_does_not_submit(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 3}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 1; + resource.localWaitCke = 332; + plan.syncResources.push_back(resource); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeEndpointOnlyLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2; + channel.ctx.raw[0] = 0xc0; + lowerLayer.channels.push_back(channel); + + lowerLayer.xnClears.push_back({1, 1961, 1}); + lowerLayer.ckeClears.push_back({1, 332, 3}); + + TileXRCcuRemoteXnBindingProof proof; + proof.dieId = 1; + proof.channelId = 2; + proof.localXn = 1961; + proof.remoteXn = 2361; + proof.notifyCke = 364; + proof.peerRank = 0; + proof.peerExchangeObserved = true; + proof.localWaitCke = 332; + proof.endpointRouteVerified = true; + lowerLayer.remoteXnBindings.push_back(proof); + return lowerLayer; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeEndpointOnlyLowerLayer(); + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + const int ret = TileXRCcuInstallHardware(request, &evidence, &installReport); + if (ret != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "endpoint-only remote XN proof unexpectedly completed install: " + << installReport.message << "\n"; + return 5; + } + if (evidence.remoteXnBound || evidence.channelBindingsInstalled || + installReport.remoteXn.satisfied || installReport.channelBinding.satisfied) { + std::cerr << "endpoint-only remote XN proof produced submit evidence\n"; + return 6; + } + if (installReport.remoteXn.message.find("channel resource owner") == std::string::npos || + installReport.channelBinding.message.find("channel resource owner") == std::string::npos) { + std::cerr << "weak endpoint-only diagnostic remote=\"" + << installReport.remoteXn.message << "\" channel=\"" + << installReport.channelBinding.message << "\"\n"; + return 7; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "endpoint-only remote XN proof became submit-ready\n"; + return 8; + } + if (!submitTasks.empty() || providerReport.submitReady) { + std::cerr << "submit-ready state leaked from endpoint-only remote XN proof\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_peer_exchange_with_channel_owner_but_without_transport_exchange_does_not_submit(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_install_provider.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void*, void*) + { + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 3}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 1; + resource.localWaitCke = 332; + plan.syncResources.push_back(resource); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + return plan; + } + + TileXRCcuLowerLayerInstallPlan MakeLowerLayer() + { + TileXRCcuLowerLayerInstallPlan lowerLayer; + lowerLayer.msidTokens.push_back({1, 0x45, 0x1234, 0x5678}); + + TileXRCcuPfeInstall pfe; + pfe.dieId = 1; + pfe.pfeOffset = 2; + pfe.ctx.raw[0] = 0xa0; + lowerLayer.pfes.push_back(pfe); + + TileXRCcuJettyInstall jetty; + jetty.dieId = 1; + jetty.startJettyCtxId = 9; + jetty.ctxs.resize(1); + jetty.ctxs[0].raw[0] = 0xb0; + lowerLayer.jettys.push_back(jetty); + + TileXRCcuChannelInstall channel; + channel.dieId = 1; + channel.channelId = 2; + channel.ctx.raw[0] = 0xc0; + lowerLayer.channels.push_back(channel); + + lowerLayer.xnClears.push_back({1, 1961, 1}); + lowerLayer.ckeClears.push_back({1, 332, 3}); + + TileXRCcuRemoteXnBindingProof proof; + proof.dieId = 1; + proof.channelId = 2; + proof.localXn = 1961; + proof.remoteXn = 2361; + proof.notifyCke = 364; + proof.peerRank = 0; + proof.peerExchangeObserved = true; + proof.localWaitCke = 332; + proof.endpointRouteVerified = true; + proof.channelResourceOwnerVerified = true; + proof.transportResourceExchangeVerified = false; + lowerLayer.remoteXnBindings.push_back(proof); + return lowerLayer; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuInstallManifest manifest; + TileXRCcuInstallManifestReport manifestReport; + if (TileXRCcuBuildInstallManifest(package, &manifest, &manifestReport) != TILEXR_SUCCESS) { + std::cerr << "manifest build failed: " << manifestReport.message << "\n"; + return 3; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 4; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuLowerLayerInstallPlan lowerLayer = MakeLowerLayer(); + TileXRCcuRepositoryInstallReceipt receipt; + TileXRCcuInstallRequest request; + request.package = &package; + request.manifest = &manifest; + request.deviceId = 3; + request.rank = 1; + request.provider = "unit-test-public-install-provider"; + request.offlineOnly = false; + request.driverAdapter = &adapter; + request.repositoryMemoryOps = memoryOps; + request.repositoryMemoryUserData = &state; + request.repositoryReceipt = &receipt; + request.lowerLayerPlan = &lowerLayer; + + TileXRCcuHardwareInstallEvidence evidence; + TileXRCcuInstallProviderReport installReport; + const int ret = TileXRCcuInstallHardware(request, &evidence, &installReport); + if (ret != TILEXR_ERROR_NOT_FOUND) { + std::cerr << "missing transport exchange proof unexpectedly completed install: " + << installReport.message << "\n"; + return 5; + } + if (evidence.remoteXnBound || evidence.channelBindingsInstalled || + installReport.remoteXn.satisfied || installReport.channelBinding.satisfied) { + std::cerr << "missing transport exchange proof produced submit evidence\n"; + return 6; + } + if (installReport.remoteXn.message.find("transport resource exchange") == std::string::npos || + installReport.channelBinding.message.find("transport resource exchange") == std::string::npos) { + std::cerr << "weak transport-exchange diagnostic remote=\"" + << installReport.remoteXn.message << "\" channel=\"" + << installReport.channelBinding.message << "\"\n"; + return 7; + } + + std::vector submitTasks; + TileXRCcuProviderReport providerReport; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &providerReport) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing transport exchange proof became submit-ready\n"; + return 8; + } + if (!submitTasks.empty() || providerReport.submitReady) { + std::cerr << "submit-ready state leaked from missing transport exchange proof\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_install_provider_layer_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = INSTALL_HEADER.read_text(encoding="utf-8") + source = INSTALL_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_install_provider.h", cmake) + self.assertIn("ccu/tilexr_ccu_install_provider.cpp", cmake) + self.assertIn("struct TileXRCcuInstallRequest", header) + self.assertIn("struct TileXRCcuInstallStepEvidence", header) + self.assertIn("struct TileXRCcuInstallProviderReport", header) + self.assertIn("TileXRCcuBuildInstallEvidence", header) + self.assertIn("TileXRCcuInstallHardware", header) + self.assertIn("TileXRCcuHardwareInstallEvidence", header) + self.assertIn("deviceId", header) + self.assertIn("rank", header) + self.assertIn("provider", header) + self.assertIn("offlineOnly", header) + self.assertIn("driverAdapter", header) + self.assertIn("repositoryMemoryOps", header) + self.assertIn("repositoryMemoryUserData", header) + self.assertIn("repositoryInstallOptions", header) + self.assertIn("repositoryReceipt", header) + self.assertIn("TileXRCcuInstallOrder", header) + self.assertIn("installOrder", header) + self.assertIn("TileXRCcuLowerLayerInstallPlan", header) + self.assertIn("TileXRCcuMsidTokenInstall", header) + self.assertIn("TileXRCcuPfeInstall", header) + self.assertIn("TileXRCcuJettyInstall", header) + self.assertIn("TileXRCcuChannelInstall", header) + self.assertIn("TileXRCcuCkeClearInstall", header) + self.assertIn("TileXRCcuXnClearInstall", header) + self.assertIn("TileXRCcuRemoteXnBindingProof", header) + self.assertIn("localWaitCke", header) + self.assertIn("lowerLayerPlan", header) + self.assertIn("xnClears", header) + self.assertIn("remoteXnBindings", header) + self.assertIn("installAttempted", header) + self.assertIn("installSucceeded", header) + self.assertIn("installAttemptReceiptId", header) + self.assertIn("TileXRCcuInstallRepositoryImage", source) + self.assertIn("TileXRCcuInstallRepositoryImageWithOptions", source) + self.assertIn("InstallLowerLayerResources", source) + self.assertIn("InstallLowerLayerFirst", source) + self.assertIn("ValidateRemoteXnExchangeBindingProof", source) + self.assertIn("local wait CKE", source) + self.assertIn("remote notify CKE", source) + self.assertIn("InstallMsidToken", source) + self.assertIn("InstallPfeCtx", source) + self.assertIn("InstallJettyCtx", source) + self.assertIn("InstallChannelCtxV1", source) + self.assertIn("const uint32_t expectedChannelCount", source) + self.assertIn("CountInstalledRemoteBindingChannels", source) + self.assertIn("installedChannelCount >= expectedChannelCount", source) + self.assertIn("InstallXnRange", source) + self.assertIn("ClearCkeRange", source) + self.assertIn("BuildRepositoryInstallReceiptId", source) + self.assertIn("repository instruction image installed via SET_INSTRUCTION", source) + self.assertIn("ValidateInstallRequestScope", source) + self.assertIn("ValidateInstallReceipt", source) + self.assertIn("install attempt did not succeed", source) + self.assertIn("device scope mismatch", source) + self.assertIn("rank scope mismatch", source) + self.assertIn("provider scope mismatch", source) + self.assertIn("no public no-hcomm CCU install provider", source) + self.assertIn("offline install evidence cannot be public verified", source) + self.assertIn("install attempt receipt", source) + self.assertIn("RejectOfflinePublicVerified", source) + self.assertIn("TileXRCcuInstallManifest", header) + self.assertIn("TileXRCcuInstallRequirement", header) + self.assertIn("TileXRCcuBuildInstallManifest", header) + self.assertIn("ValidateInstallManifestScope", source) + self.assertIn("ValidateInstallRequestManifest", source) + self.assertIn("install manifest fingerprint mismatch", source) + self.assertIn("install manifest requirement kind mismatch", source) + self.assertIn("install manifest hardware requirement mismatch", source) + self.assertIn("install manifest mission requirement mismatch", source) + self.assertIn("ValidateMissionLaunchDescriptorProof", source) + self.assertIn("launch task descriptor", source) + self.assertIn("install manifest channel requirement mismatch", source) + self.assertIn("ValidatePublicVerifiedStepScope", source) + self.assertIn("public verified evidence scope is incomplete", source) + self.assertIn("launch install scope is stale", source) + + combined = header + "\n" + source + self.assertNotIn("SET_MISSION_CTX", combined) + self.assertNotIn("TILEXR_CCU_U_OP_SET_MISSION_CTX", combined) + self.assertNotIn("258", combined) + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_launch_package.py b/tests/ccu/test_tilexr_ccu_launch_package.py new file mode 100644 index 0000000..dda0ace --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_launch_package.py @@ -0,0 +1,501 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PACKAGE_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_launch_package.h" +PACKAGE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_launch_package.cpp" +REPOSITORY_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_repository.cpp" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" +PRODUCER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.cpp" +BARRIER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL, 0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(plan, &package, &report) != TILEXR_SUCCESS) { + std::cerr << "launch package build failed: " << report.message << "\n"; + return 1; + } + + if (package.tasks.size() != 2 || package.program.sqeLoad.size() != 13 || + package.program.sync.size() != 11 || package.repository.instructions.size() != 170) { + std::cerr << "unexpected package sizes\n"; + return 2; + } + if (package.tasks[0].instStartId != 489 || package.tasks[0].instCnt != 13 || + package.tasks[0].argSize != 13 || package.tasks[0].key != 0x059b0f03U || + package.tasks[0].args[0] != 0x100051152e00ULL) { + std::cerr << "unexpected sqe-load mission task\n"; + return 3; + } + if (package.tasks[1].instStartId != 502 || package.tasks[1].instCnt != 143 || + package.tasks[1].argSize != 13 || package.tasks[1].key != 0x059b0f03U) { + std::cerr << "unexpected sync mission task\n"; + return 4; + } + if (package.repository.sqeLoadOffset != 14 || package.repository.syncOffset != 27 || + package.repository.instructions[14].words[0] != 0x0000000007a90001ULL || + package.repository.instructions[27].words[0] != 0x0000000007a90001ULL || + package.repository.instructions[31].words[0] != 0x0001016c00000802ULL || + package.repository.instructions[32].words[0] != 0x000007a90939100dULL || + package.repository.instructions[35].words[0] != 0x0000000000010802ULL) { + std::cerr << "unexpected repository image\n"; + return 5; + } + if (!package.requiresHardwareInstall) { + std::cerr << "package should still require hardware install\n"; + return 6; + } + if (report.taskCount != 2 || report.repositoryCount != 170 || + report.installedInstructionCount != 24 || report.message != "ok") { + std::cerr << "unexpected package report\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_rejects_invalid_inputs_without_partial_outputs(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_launch_package.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(plan, nullptr, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null package output accepted\n"; + return 1; + } + if (report.message.find("package") == std::string::npos) { + std::cerr << "null output diagnostic was weak: " << report.message << "\n"; + return 2; + } + + TileXRCcuLaunchPackage package; + plan.syncResources[0].bindingCount = 0; + if (TileXRCcuBuildLaunchPackage(plan, &package, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid producer state accepted\n"; + return 3; + } + if (!package.tasks.empty() || !package.repository.instructions.empty()) { + std::cerr << "failed build left partial package state\n"; + return 4; + } + if (report.message.find("binding") == std::string::npos) { + std::cerr << "invalid producer diagnostic was weak: " << report.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_builds_pure_barrier_without_sqe_load_task(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_launch_package.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {0, 1, 0x12345678U, true}; + plan.kernelLocalXn = {0, 1, 1}; + plan.kernelLocalCke = {0, 1, 1}; + plan.kernelLocalMission = {0, 1, 1}; + plan.instructionWindow = {0, 1, 2, 1, 2}; + plan.syncResources.push_back({0, 1, 2, 1, 1, 1, 1, 1, 1}); + plan.taskWindows.push_back({0, 1, 2, 13, {}}); + + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(plan, &package, &report) != TILEXR_SUCCESS) { + std::cerr << "pure barrier package build failed: " << report.message << "\n"; + return 1; + } + if (package.tasks.size() != 1 || !package.program.sqeLoad.empty() || + package.program.sync.size() != 2 || package.repository.instructions.size() != 2) { + std::cerr << "pure barrier package size mismatch\n"; + return 2; + } + if (package.tasks[0].instStartId != 1 || package.tasks[0].instCnt != 2 || + package.tasks[0].argSize != 13 || package.tasks[0].args[0] != 0) { + std::cerr << "pure barrier task mismatch\n"; + return 3; + } + if (package.repository.sqeLoadCount != 0 || package.repository.syncOffset != 0 || + package.repository.syncCount != 2 || + package.repository.instructions[0].words[0] != package.program.sync[0].words[0] || + package.repository.instructions[0].words[1] != package.program.sync[0].words[1] || + package.repository.instructions[1].words[0] != package.program.sync[1].words[0] || + package.repository.instructions[1].words[1] != package.program.sync[1].words[1]) { + std::cerr << "pure barrier repository mismatch\n"; + return 4; + } + if (report.taskCount != 1 || report.installedInstructionCount != 2 || + report.message != "ok") { + std::cerr << "pure barrier report mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_install_scope_records_current_package_fingerprint(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_launch_package.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + if (TileXRCcuBindLaunchPackageInstallScope( + nullptr, 3, 1, "unit-test-public-install-provider") != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null package scope bind was accepted\n"; + return 1; + } + + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &report) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << report.message << "\n"; + return 2; + } + if (TileXRCcuBindLaunchPackageInstallScope(&package, 3, 1, "") != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty provider scope bind was accepted\n"; + return 3; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 4; + } + + const uint64_t fingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (package.installScope.deviceId != 3 || + package.installScope.rank != 1 || + package.installScope.provider != "unit-test-public-install-provider" || + package.installScope.packageFingerprint != fingerprint) { + std::cerr << "unexpected install scope binding\n"; + return 5; + } + + package.tasks[0].key ^= 0x1U; + if (TileXRCcuComputeLaunchPackageFingerprint(package) == fingerprint) { + std::cerr << "mutated package kept old fingerprint\n"; + return 6; + } + if (package.installScope.packageFingerprint == TileXRCcuComputeLaunchPackageFingerprint(package)) { + std::cerr << "install scope fingerprint silently tracked mutation\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_fingerprint_changes_for_local_wait_cke(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_launch_package.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan(uint16_t localWaitCke) + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 4}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 3; + resource.localWaitCke = localWaitCke; + resource.localWaitMask = 1; + resource.remoteNotifyMask = 1; + plan.syncResources.push_back(resource); + + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage packageA; + TileXRCcuLaunchPackage packageB; + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(MakePlan(332), &packageA, &report) != TILEXR_SUCCESS || + TileXRCcuBuildLaunchPackage(MakePlan(333), &packageB, &report) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << report.message << "\n"; + return 1; + } + const uint64_t fingerprintA = TileXRCcuComputeLaunchPackageFingerprint(packageA); + const uint64_t fingerprintB = TileXRCcuComputeLaunchPackageFingerprint(packageB); + if (fingerprintA == 0 || fingerprintB == 0 || fingerprintA == fingerprintB) { + std::cerr << "local wait CKE did not affect launch package fingerprint\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_fingerprint_changes_for_kernel_local_gsa(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_launch_package.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan(uint16_t gsaStart) + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalGsa = {1, gsaStart, 1}; + plan.kernelLocalCke = {1, 332, 4}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3, 332, 1, 1}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage packageA; + TileXRCcuLaunchPackage packageB; + TileXRCcuLaunchPackageReport report; + if (TileXRCcuBuildLaunchPackage(MakePlan(510), &packageA, &report) != TILEXR_SUCCESS || + TileXRCcuBuildLaunchPackage(MakePlan(511), &packageB, &report) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << report.message << "\n"; + return 1; + } + const uint64_t fingerprintA = TileXRCcuComputeLaunchPackageFingerprint(packageA); + const uint64_t fingerprintB = TileXRCcuComputeLaunchPackageFingerprint(packageB); + if (fingerprintA == 0 || fingerprintB == 0 || fingerprintA == fingerprintB) { + std::cerr << "kernel-local GSA did not affect launch package fingerprint\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_launch_package_layer_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = PACKAGE_HEADER.read_text(encoding="utf-8") + source = PACKAGE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_launch_package.h", cmake) + self.assertIn("ccu/tilexr_ccu_launch_package.cpp", cmake) + self.assertIn("struct TileXRCcuLaunchPackage", header) + self.assertIn("struct TileXRCcuLaunchPackageReport", header) + self.assertIn("TileXRCcuBuildLaunchPackage", header) + self.assertIn("TileXRCcuLaunchInstallScope", header) + self.assertIn("installScope", header) + self.assertIn("TileXRCcuBindLaunchPackageInstallScope", header) + self.assertIn("requiresHardwareInstall", header) + self.assertIn("tilexr_ccu_repository.h", header) + self.assertIn("resource.localWaitCke", source) + self.assertIn("resource.localWaitMask", source) + self.assertIn("resource.remoteNotifyMask", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_lower_layer_payloads.py b/tests/ccu/test_tilexr_ccu_lower_layer_payloads.py new file mode 100644 index 0000000..cba54f7 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_lower_layer_payloads.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PAYLOAD_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_lower_layer_payloads.h" +PAYLOAD_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_lower_layer_payloads.cpp" +ABI_CONSTANTS_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_abi_constants.h" +HCOMM_ORACLE_SOURCE = REPO_ROOT / "tests" / "ccu" / "ccu_lower_layer_payload_hcomm_oracle.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuLowerLayerPayloadsTest(unittest.TestCase): + def compile_and_run(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "lower_layer_payloads_test.cpp" + test_bin = temp_path / "lower_layer_payloads_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(PAYLOAD_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def test_packers_match_lower_layer_pfe_jetty_and_channel_v1_layouts(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_payloads.h" + + #include + #include + + using namespace TileXR; + + uint16_t Read16(const uint8_t* raw, uint32_t offset) + { + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1]) << 8U); + } + + int main() + { + TileXRCcuLowerLayerPayloadReport report; + + TileXRCcuPfeCtx pfe; + TileXRCcuPfeCtxSpec pfeSpec; + pfeSpec.startJettyId = 0x1234; + pfeSpec.jettyCount = 5; + pfeSpec.startLocalJettyCtxId = 0x22; + if (TileXRCcuBuildPfeCtx(pfeSpec, &pfe, &report) != TILEXR_SUCCESS) { + std::cerr << "pfe build failed: " << report.message << "\n"; + return 1; + } + if (Read16(pfe.raw, 0) != 0x1234 || + Read16(pfe.raw, 2) != static_cast(4U | (0x22U << 7U)) || + Read16(pfe.raw, 4) != 0 || Read16(pfe.raw, 6) != 0) { + std::cerr << "pfe layout mismatch\n"; + return 2; + } + + TileXRCcuLocalJettyCtxData jetty; + TileXRCcuLocalJettyCtxSpec jettySpec; + jettySpec.dieId = 1; + jettySpec.pfeId = 3; + jettySpec.doorbellVa = 0x1122334455667788ULL; + jettySpec.doorbellTokenId = 0x000abcdeU; + jettySpec.doorbellTokenValue = 0x89abcdefU; + jettySpec.sqDepth = 16; + jettySpec.wqeBasicBlockStartId = 0x9a; + if (TileXRCcuBuildLocalJettyCtx(jettySpec, &jetty, &report) != TILEXR_SUCCESS) { + std::cerr << "jetty build failed: " << report.message << "\n"; + return 3; + } + if (Read16(jetty.raw, 0) != 0x7788 || Read16(jetty.raw, 2) != 0x5566 || + Read16(jetty.raw, 4) != 0x3344 || Read16(jetty.raw, 6) != 0x1122 || + Read16(jetty.raw, 8) != 0xde73 || Read16(jetty.raw, 10) != 0xfabc || + Read16(jetty.raw, 12) != 0xbcde || Read16(jetty.raw, 14) != 0x689a || + Read16(jetty.raw, 16) != 0 || Read16(jetty.raw, 18) != 0 || + Read16(jetty.raw, 20) != 0 || Read16(jetty.raw, 22) != 0xa000 || + Read16(jetty.raw, 24) != 0x0009 || Read16(jetty.raw, 26) != 0 || + Read16(jetty.raw, 28) != 0 || Read16(jetty.raw, 30) != 0) { + std::cerr << "jetty layout mismatch\n"; + return 4; + } + + TileXRCcuChannelCtxDataV1 channel; + TileXRCcuChannelCtxV1Spec channelSpec; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + channelSpec.remoteEid[i] = static_cast(0x10 + i); + } + channelSpec.tpn = 0x00ab5678U; + channelSpec.sourcePfeId = 5; + channelSpec.startJettyId = 0x0234; + channelSpec.jettyCount = 7; + channelSpec.dieId = 1; + channelSpec.memoryTokenId = 0x000abcdeU; + channelSpec.memoryTokenValue = 0x89abcdefU; + channelSpec.remoteCcuVa = 0x000123456789ab00ULL; + if (TileXRCcuBuildChannelCtxV1(channelSpec, &channel, &report) != TILEXR_SUCCESS) { + std::cerr << "channel build failed: " << report.message << "\n"; + return 5; + } + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + if (channel.raw[i] != static_cast(0x10 + i)) { + std::cerr << "channel eid mismatch\n"; + return 6; + } + } + const uint64_t dstVa = channelSpec.remoteCcuVa >> TILEXR_CCU_REMOTE_CCU_VA_SHIFT; + if (Read16(channel.raw, 16) != 0x5678 || + Read16(channel.raw, 18) != 0x45ab || + Read16(channel.raw, 20) != 0x6023 || + Read16(channel.raw, 22) != 0xcde8 || + Read16(channel.raw, 24) != 0xefab || + Read16(channel.raw, 26) != 0xabcd || + Read16(channel.raw, 28) != static_cast(0x0089U | ((dstVa & 0xffU) << 8U)) || + Read16(channel.raw, 30) != static_cast((dstVa >> 8U) & 0xffffU) || + Read16(channel.raw, 32) != static_cast((dstVa >> 24U) & 0xffffU) || + Read16(channel.raw, 34) != static_cast(((dstVa >> 40U) & 0x1U) | 0x2U) || + Read16(channel.raw, 36) != 0 || Read16(channel.raw, 62) != 0) { + std::cerr << "channel layout mismatch\n"; + return 7; + } + + if (TileXRCcuBuildPfeCtx({0, 0, 0}, &pfe, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuBuildLocalJettyCtx({}, &jetty, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuBuildChannelCtxV1({}, &channel, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid lower-layer payload specs accepted\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_packers_accept_plaintext_zero_token_values_from_tilexr_udma(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_payloads.h" + + #include + #include + + using namespace TileXR; + + uint16_t Read16(const uint8_t* raw, uint32_t offset) + { + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1]) << 8U); + } + + int main() + { + TileXRCcuLowerLayerPayloadReport report; + + TileXRCcuLocalJettyCtxData jetty; + TileXRCcuLocalJettyCtxSpec jettySpec; + jettySpec.dieId = 0; + jettySpec.pfeId = 2; + jettySpec.doorbellVa = 0x1020304050607080ULL; + jettySpec.doorbellTokenId = 0x12345U; + jettySpec.doorbellTokenValue = 0; + jettySpec.sqDepth = 8; + if (TileXRCcuBuildLocalJettyCtx(jettySpec, &jetty, &report) != TILEXR_SUCCESS) { + std::cerr << "zero doorbell token value rejected: " << report.message << "\n"; + return 1; + } + if (Read16(jetty.raw, 10) != 0x0123 || + Read16(jetty.raw, 12) != 0 || + (Read16(jetty.raw, 14) & 0x0fffU) != 0) { + std::cerr << "zero doorbell token value packed incorrectly\n"; + return 2; + } + + jettySpec.doorbellTokenId = 0; + if (TileXRCcuBuildLocalJettyCtx(jettySpec, &jetty, &report) != TILEXR_SUCCESS) { + std::cerr << "zero doorbell token id rejected: " << report.message << "\n"; + return 5; + } + if ((Read16(jetty.raw, 8) & 0x0040U) == 0 || + (Read16(jetty.raw, 8) & 0xff00U) != 0 || + (Read16(jetty.raw, 10) & 0x0fffU) != 0) { + std::cerr << "zero doorbell token id packed incorrectly\n"; + return 6; + } + + TileXRCcuChannelCtxDataV1 channel; + TileXRCcuChannelCtxV1Spec channelSpec; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + channelSpec.remoteEid[i] = static_cast(0xa0 + i); + } + channelSpec.tpn = 0x13579U; + channelSpec.sourcePfeId = 2; + channelSpec.startJettyId = 0x44; + channelSpec.jettyCount = 1; + channelSpec.dieId = 0; + channelSpec.memoryTokenId = 0x12345U; + channelSpec.memoryTokenValue = 0; + channelSpec.remoteCcuVa = 0x0000001234000000ULL; + if (TileXRCcuBuildChannelCtxV1(channelSpec, &channel, &report) != TILEXR_SUCCESS) { + std::cerr << "zero memory token value rejected: " << report.message << "\n"; + return 7; + } + if ((Read16(channel.raw, 24) & 0xff00U) != 0 || + Read16(channel.raw, 26) != 0 || + (Read16(channel.raw, 28) & 0x00ffU) != 0 || + (Read16(channel.raw, 34) & 0x2U) == 0) { + std::cerr << "zero memory token value packed incorrectly\n"; + return 8; + } + + channelSpec.memoryTokenId = 0; + if (TileXRCcuBuildChannelCtxV1(channelSpec, &channel, &report) != TILEXR_SUCCESS) { + std::cerr << "zero memory token id rejected: " << report.message << "\n"; + return 9; + } + if ((Read16(channel.raw, 22) & 0xfff0U) != 0 || + (Read16(channel.raw, 24) & 0x00ffU) != 0 || + (Read16(channel.raw, 34) & 0x2U) == 0) { + std::cerr << "zero memory token id packed incorrectly\n"; + return 10; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_lower_layer_payload_packers_are_wired_without_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = PAYLOAD_HEADER.read_text(encoding="utf-8") + source = PAYLOAD_SOURCE.read_text(encoding="utf-8") + oracle = HCOMM_ORACLE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_lower_layer_payloads.h", cmake) + self.assertIn("ccu/tilexr_ccu_lower_layer_payloads.cpp", cmake) + self.assertIn("TileXRCcuPfeCtxSpec", header) + self.assertIn("TileXRCcuLocalJettyCtxSpec", header) + self.assertIn("TileXRCcuChannelCtxV1Spec", header) + self.assertIn("TileXRCcuBuildPfeCtx", header) + self.assertIn("TileXRCcuBuildLocalJettyCtx", header) + self.assertIn("TileXRCcuBuildChannelCtxV1", header) + self.assertIn("tilexr_ccu_abi_constants.h", header) + self.assertIn("TILEXR_CCU_REMOTE_CCU_VA_SHIFT", ABI_CONSTANTS_HEADER.read_text(encoding="utf-8")) + self.assertIn("BuildHcommPfeCtx", oracle) + self.assertIn("BuildHcommLocalJettyCtx", oracle) + self.assertIn("BuildHcommChannelCtxV1", oracle) + + combined = header + "\n" + source + "\n" + oracle + for needle in [ + "#include + #include + + using namespace TileXR; + + uint16_t Read16(const uint8_t* raw, uint32_t offset) + { + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1]) << 8U); + } + + int main() + { + TileXRCcuLowerLayerPlanSpec spec; + spec.msidToken.dieId = 1; + spec.msidToken.msId = 0x55; + spec.msidToken.tokenId = 0x45678; + spec.msidToken.tokenValue = 0; + spec.msidToken.valid = true; + + spec.pfe.dieId = 1; + spec.pfe.pfeOffset = 3; + spec.pfe.startJettyId = 0x120; + spec.pfe.startLocalJettyCtxId = 0x21; + + TileXRCcuLowerLayerJettySpec jetty0; + jetty0.dieId = 1; + jetty0.pfeId = 3; + jetty0.startJettyCtxId = 0x21; + jetty0.doorbellVa = 0x1122334455667788ULL; + jetty0.doorbellTokenId = 0x45678; + jetty0.doorbellTokenValue = 0; + jetty0.sqDepth = 16; + jetty0.wqeBasicBlockStartId = 0x40; + spec.jettys.push_back(jetty0); + + TileXRCcuLowerLayerJettySpec jetty1 = jetty0; + jetty1.startJettyCtxId = 0x22; + jetty1.doorbellVa = 0x2122334455667788ULL; + jetty1.wqeBasicBlockStartId = 0x44; + spec.jettys.push_back(jetty1); + + TileXRCcuLowerLayerChannelSpec channel; + channel.dieId = 1; + channel.channelId = 5; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + channel.remoteEid[i] = static_cast(0x20 + i); + } + channel.tpn = 0x123456; + channel.sourcePfeId = 3; + channel.startJettyId = 0x120; + channel.memoryTokenId = 0xabcde; + channel.memoryTokenValue = 0; + channel.remoteCcuVa = 0x0001234567800000ULL; + spec.channels.push_back(channel); + + spec.xnClear.dieId = 1; + spec.xnClear.startXnId = 0x1f0; + spec.xnClear.count = 3; + spec.xnClear.valid = true; + + spec.ckeClear.dieId = 1; + spec.ckeClear.startCkeId = 0x180; + spec.ckeClear.count = 2; + spec.ckeClear.valid = true; + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerInstallPlan(spec, &plan, &report) != TILEXR_SUCCESS) { + std::cerr << "plan build failed: " << report.message << "\n"; + return 1; + } + if (report.msidTokenCount != 1 || report.pfeCount != 1 || + report.jettyCount != 1 || report.localJettyCtxCount != 2 || + report.channelCount != 1 || report.ckeClearCount != 1) { + std::cerr << "unexpected plan report counts\n"; + return 2; + } + if (plan.msidTokens.size() != 1 || plan.pfes.size() != 1 || + plan.jettys.size() != 1 || plan.jettys[0].ctxs.size() != 2 || + plan.channels.size() != 1 || plan.xnClears.size() != 1 || + plan.ckeClears.size() != 1) { + std::cerr << "unexpected install plan shape\n"; + return 3; + } + if (plan.msidTokens[0].tokenValue != 0 || + plan.msidTokens[0].tokenId != 0x45678U || + plan.xnClears[0].startXnId != 0x1f0 || + plan.xnClears[0].count != 3 || + plan.ckeClears[0].startCkeId != 0x180 || + plan.ckeClears[0].count != 2) { + std::cerr << "scalar plan fields mismatch\n"; + return 4; + } + if (Read16(plan.pfes[0].ctx.raw, 0) != 0x120 || + Read16(plan.pfes[0].ctx.raw, 2) != static_cast(1U | (0x21U << 7U))) { + std::cerr << "pfe payload mismatch\n"; + return 5; + } + if (Read16(plan.jettys[0].ctxs[0].raw, 0) != 0x7788 || + Read16(plan.jettys[0].ctxs[1].raw, 0) != 0x7788 || + Read16(plan.jettys[0].ctxs[0].raw, 8) != 0x7873) { + std::cerr << "jetty payload mismatch\n"; + return 6; + } + if (plan.channels[0].ctx.raw[0] != 0x20 || + plan.channels[0].ctx.raw[15] != 0x2f || + Read16(plan.channels[0].ctx.raw, 16) != 0x3456) { + std::cerr << "channel payload mismatch\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_can_use_hcomm_compatible_wqe_stride_for_direct_ccu_experiment(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 2}; + allocation.localXn = {1, 0x120, 2}; + allocation.remoteXn = {1, 0x240, 2}; + allocation.notifyCke = {1, 0x330, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remote0.remoteNotifyCke = 0x360; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remote1.remoteNotifyCke = 0x361; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.routes.size() != 2 || + snapshot.routes[0].wqeBasicBlockStartId != 0 || + snapshot.routes[1].wqeBasicBlockStartId != 256) { + std::cerr << "hcomm-compatible WQE stride not applied\n"; + return 2; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE"] = "hcomm_cap" + + result = self.compile_and_run(code, env=env) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_preserves_explicit_peer_rank_from_remote_buffer(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 7, 1}; + allocation.localXn = {1, 0x120, 1}; + allocation.remoteXn = {1, 0x240, 1}; + allocation.notifyCke = {1, 0x330, 1}; + + TileXRCcuRemoteCcuBufferInfo remote; + remote.peerRank = 1; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + std::vector remoteCcuBuffers {remote}; + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.routes.size() != 1 || snapshot.routes[0].peerRank != 1) { + std::cerr << "explicit peer rank from remote buffer was not preserved\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_can_use_hcomm_die_pfe_offset_for_direct_ccu_experiment(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + basic.caps.cap4 = 15U; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 2}; + allocation.localXn = {1, 0x120, 2}; + allocation.remoteXn = {1, 0x240, 2}; + allocation.notifyCke = {1, 0x330, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.pfeId != 2 || snapshot.pfeOffset != 18) { + std::cerr << "hcomm die pfe offset not applied: pfeId=" << snapshot.pfeId + << " pfeOffset=" << snapshot.pfeOffset << "\n"; + return 2; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE"] = "hcomm_die" + + result = self.compile_and_run(code, env=env) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_can_use_hcomm_ordered_pfe_partition_for_direct_ccu_experiment(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 3, 2}; + allocation.localXn = {1, 0x120, 2}; + allocation.remoteXn = {1, 0x240, 2}; + allocation.notifyCke = {1, 0x330, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.pfeId != 3 || + snapshot.startLocalJettyCtxId != 0 || + snapshot.startJettyId != 1024 || + snapshot.pfeJettyCount != 23 || + snapshot.routes.size() != 2) { + std::cerr << "hcomm ordered pfe partition not applied: pfeId=" << snapshot.pfeId + << " startLocalJettyCtxId=" << snapshot.startLocalJettyCtxId + << " startJettyId=" << snapshot.startJettyId + << " pfeJettyCount=" << snapshot.pfeJettyCount << "\n"; + return 2; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION"] = "hcomm" + + result = self.compile_and_run(code, env=env) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_can_use_hcomm_fe_id_pfe_partition_for_direct_ccu_experiment(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 3, 2}; + allocation.localXn = {1, 0x120, 2}; + allocation.remoteXn = {1, 0x240, 2}; + allocation.notifyCke = {1, 0x330, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.pfeId != 3 || + snapshot.startLocalJettyCtxId != 69 || + snapshot.startJettyId != 1093 || + snapshot.pfeJettyCount != 23) { + std::cerr << "hcomm fe-id pfe partition not applied: pfeId=" << snapshot.pfeId + << " startLocalJettyCtxId=" << snapshot.startLocalJettyCtxId + << " startJettyId=" << snapshot.startJettyId + << " pfeJettyCount=" << snapshot.pfeJettyCount << "\n"; + return 2; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION"] = "hcomm_fe_id" + + result = self.compile_and_run(code, env=env) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_builds_lower_layer_install_plan_from_transport_snapshot(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + uint16_t Read16(const uint8_t* raw, uint32_t offset) + { + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1]) << 8U); + } + + int main() + { + TileXRCcuLowerLayerTransportSnapshot snapshot; + snapshot.msidToken.dieId = 0; + snapshot.msidToken.msId = 0x9; + snapshot.msidToken.tokenId = 0x12345; + snapshot.msidToken.tokenValue = 0; + snapshot.msidToken.valid = true; + snapshot.dieId = 0; + snapshot.pfeOffset = 2; + snapshot.pfeId = 2; + snapshot.startJettyId = 0x80; + snapshot.startLocalJettyCtxId = 0x10; + snapshot.xnStartId = 0x1a0; + snapshot.xnCount = 6; + snapshot.ckeStartId = 0x220; + snapshot.ckeCount = 2; + + TileXRCcuLowerLayerTransportRoute route0; + route0.channelId = 7; + route0.remoteXnId = 0x2a0; + route0.remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + route0.remoteEid[i] = static_cast(0x40 + i); + } + route0.tpn = 0x010203; + route0.doorbellVa = 0x1111222233334444ULL; + route0.doorbellTokenId = 0x12345; + route0.doorbellTokenValue = 0; + route0.sqDepth = 8; + route0.wqeBasicBlockStartId = 0x30; + route0.memoryTokenId = 0x23456; + route0.memoryTokenValue = 0; + route0.remoteCcuVa = 0x0000009234000000ULL; + route0.peerRank = 7; + route0.endpointRouteVerified = true; + snapshot.routes.push_back(route0); + + TileXRCcuLowerLayerTransportRoute route1 = route0; + route1.channelId = 8; + route1.remoteXnId = 0x2a1; + route1.remoteNotifyCke = 0x361; + route1.remoteEid[0] = 0x50; + route1.tpn = 0x010204; + route1.doorbellVa = 0x5555666677778888ULL; + route1.wqeBasicBlockStartId = 0x34; + route1.memoryTokenId = 0x23457; + route1.remoteCcuVa = 0x0000009334000000ULL; + route1.peerRank = 9; + route1.endpointRouteVerified = true; + snapshot.routes.push_back(route1); + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "transport snapshot build failed: " << report.message << "\n"; + return 1; + } + if (plan.msidTokens.size() != 1 || plan.pfes.size() != 1 || + plan.jettys.size() != 1 || plan.jettys[0].ctxs.size() != 2 || + plan.channels.size() != 2 || plan.xnClears.size() != 2 || + plan.ckeClears.size() != 2 || plan.remoteXnBindings.size() != 2) { + std::cerr << "unexpected transport-derived plan shape\n"; + return 2; + } + if (report.channelCount != 2 || report.localJettyCtxCount != 2 || + report.ckeClearCount != 2) { + std::cerr << "unexpected transport-derived report counts\n"; + return 3; + } + if (Read16(plan.pfes[0].ctx.raw, 0) != 0x80 || + Read16(plan.pfes[0].ctx.raw, 2) != static_cast(1U | (0x10U << 7U))) { + std::cerr << "transport pfe payload mismatch\n"; + return 4; + } + if (Read16(plan.jettys[0].ctxs[0].raw, 0) != 0x4444 || + Read16(plan.jettys[0].ctxs[1].raw, 0) != 0x8888) { + std::cerr << "transport jetty payload mismatch\n"; + return 5; + } + if (plan.channels[0].channelId != 7 || plan.channels[1].channelId != 8 || + plan.channels[0].ctx.raw[0] != 0x40 || plan.channels[1].ctx.raw[0] != 0x50 || + Read16(plan.channels[0].ctx.raw, 16) != 0x0203 || + Read16(plan.channels[1].ctx.raw, 16) != 0x0204) { + std::cerr << "transport channel payload mismatch\n"; + return 6; + } + if (Read16(plan.channels[0].ctx.raw, 18) != 0x0201 || + Read16(plan.channels[0].ctx.raw, 20) != 0x0008 || + Read16(plan.channels[1].ctx.raw, 18) != 0x1201 || + Read16(plan.channels[1].ctx.raw, 20) != 0x0008) { + std::cerr << "transport channel jetty window mismatch\n"; + return 9; + } + if (plan.xnClears[0].startXnId != 0x1a0 || plan.xnClears[0].count != 6 || + plan.xnClears[1].startXnId != 0x2a0 || plan.xnClears[1].count != 2 || + plan.ckeClears[0].startCkeId != 0x220 || plan.ckeClears[0].count != 2 || + plan.ckeClears[1].startCkeId != 0x360 || plan.ckeClears[1].count != 2) { + std::cerr << "transport local/channel XN or CKE clear mismatch\n"; + return 7; + } + if (!plan.remoteXnBindings[0].peerExchangeObserved || + plan.remoteXnBindings[0].channelId != 7 || + plan.remoteXnBindings[0].localXn != 0x1a0 || + plan.remoteXnBindings[0].remoteXn != 0x2a0 || + plan.remoteXnBindings[0].notifyCke != 0x360 || + plan.remoteXnBindings[0].peerRank != 7 || + plan.remoteXnBindings[0].localWaitCke != 0x220 || + !plan.remoteXnBindings[0].endpointRouteVerified || + plan.remoteXnBindings[1].channelId != 8 || + plan.remoteXnBindings[1].localXn != 0x1a1 || + plan.remoteXnBindings[1].remoteXn != 0x2a1 || + plan.remoteXnBindings[1].notifyCke != 0x361 || + plan.remoteXnBindings[1].peerRank != 9 || + plan.remoteXnBindings[1].localWaitCke != 0x221 || + !plan.remoteXnBindings[1].endpointRouteVerified) { + std::cerr << "transport remote XN proof mismatch\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_snapshot_pfe_jetty_count_reaches_pfe_ctx(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + uint16_t Read16(const uint8_t* raw, uint32_t offset) + { + return static_cast(raw[offset]) | + static_cast(static_cast(raw[offset + 1]) << 8U); + } + + int main() + { + TileXRCcuLowerLayerTransportSnapshot snapshot; + snapshot.msidToken.dieId = 1; + snapshot.msidToken.msId = 0x9; + snapshot.msidToken.tokenId = 0x12345; + snapshot.msidToken.valid = true; + snapshot.dieId = 1; + snapshot.pfeOffset = 18; + snapshot.pfeId = 2; + snapshot.startJettyId = 1024; + snapshot.pfeJettyCount = 23; + snapshot.startLocalJettyCtxId = 0; + snapshot.xnStartId = 0x1a0; + snapshot.xnCount = 1; + snapshot.ckeStartId = 0x220; + snapshot.ckeCount = 1; + + TileXRCcuLowerLayerTransportRoute route; + route.channelId = 7; + route.remoteXnId = 0x2a0; + route.remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + route.remoteEid[i] = static_cast(0x40 + i); + } + route.tpn = 0x010203; + route.doorbellVa = 0x1111222233334444ULL; + route.doorbellTokenId = 0x12345; + route.sqDepth = 8; + route.memoryTokenId = 0x23456; + route.remoteCcuVa = 0x0000009234000000ULL; + snapshot.routes.push_back(route); + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "transport snapshot build failed: " << report.message << "\n"; + return 1; + } + if (plan.pfes.size() != 1 || plan.pfes[0].pfeOffset != 18) { + std::cerr << "pfe install shape mismatch\n"; + return 2; + } + const uint16_t pfeWord = Read16(plan.pfes[0].ctx.raw, 2); + if ((pfeWord & 0x7fU) != 22U) { + std::cerr << "pfe jetty count did not reach ctx: word=" << pfeWord << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_snapshot_installs_remote_xn_range_separately(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuLowerLayerTransportSnapshot snapshot; + snapshot.msidToken.dieId = 1; + snapshot.msidToken.msId = 0x45; + snapshot.msidToken.tokenId = 0x1234; + snapshot.msidToken.valid = true; + snapshot.dieId = 1; + snapshot.pfeOffset = 0x80; + snapshot.pfeId = 2; + snapshot.startJettyId = 0x400; + snapshot.pfeJettyCount = 2; + snapshot.startLocalJettyCtxId = 0; + snapshot.xnStartId = 0x1a0; + snapshot.xnCount = 2; + snapshot.ckeStartId = 0x220; + snapshot.ckeCount = 2; + + snapshot.routes.resize(2); + snapshot.routes[0].channelId = 7; + snapshot.routes[0].remoteXnId = 0x2a0; + snapshot.routes[0].remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + snapshot.routes[0].remoteEid[i] = static_cast(0x40 + i); + } + snapshot.routes[0].tpn = 0x010203; + snapshot.routes[0].doorbellVa = 0x1111222233334444ULL; + snapshot.routes[0].doorbellTokenId = 0x12345; + snapshot.routes[0].sqDepth = 8; + snapshot.routes[0].remoteCcuVa = 0x90000000ULL; + snapshot.routes[0].memoryTokenId = 0x1234; + + snapshot.routes[1] = snapshot.routes[0]; + snapshot.routes[1].channelId = 8; + snapshot.routes[1].remoteXnId = 0x2a1; + snapshot.routes[1].remoteNotifyCke = 0x361; + snapshot.routes[1].tpn = 0x010204; + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "transport snapshot build failed: " << report.message << "\n"; + return 1; + } + if (plan.xnClears.size() != 2) { + std::cerr << "expected local and channel remote XN clears, got " << + plan.xnClears.size() << "\n"; + return 2; + } + if (plan.xnClears[0].startXnId != 0x1a0 || plan.xnClears[0].count != 2) { + std::cerr << "local XN clear mismatch\n"; + return 3; + } + if (plan.xnClears[1].startXnId != 0x2a0 || plan.xnClears[1].count != 2) { + std::cerr << "channel remote XN clear mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_snapshot_installs_remote_notify_cke_range_separately(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuLowerLayerTransportSnapshot snapshot; + snapshot.msidToken.dieId = 1; + snapshot.msidToken.msId = 0x45; + snapshot.msidToken.tokenId = 0x1234; + snapshot.msidToken.valid = true; + snapshot.dieId = 1; + snapshot.pfeOffset = 0x80; + snapshot.pfeId = 2; + snapshot.startJettyId = 0x400; + snapshot.pfeJettyCount = 2; + snapshot.startLocalJettyCtxId = 0; + snapshot.xnStartId = 0x1a0; + snapshot.xnCount = 2; + snapshot.ckeStartId = 0x220; + snapshot.ckeCount = 3; + + snapshot.routes.resize(2); + snapshot.routes[0].channelId = 7; + snapshot.routes[0].remoteXnId = 0x2a0; + snapshot.routes[0].remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + snapshot.routes[0].remoteEid[i] = static_cast(0x40 + i); + } + snapshot.routes[0].tpn = 0x010203; + snapshot.routes[0].doorbellVa = 0x1111222233334444ULL; + snapshot.routes[0].doorbellTokenId = 0x12345; + snapshot.routes[0].sqDepth = 8; + snapshot.routes[0].remoteCcuVa = 0x90000000ULL; + snapshot.routes[0].memoryTokenId = 0x1234; + snapshot.routes[1] = snapshot.routes[0]; + snapshot.routes[1].channelId = 8; + snapshot.routes[1].remoteXnId = 0x2a1; + snapshot.routes[1].remoteNotifyCke = 0x361; + snapshot.routes[1].tpn = 0x010204; + + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "transport snapshot build failed: " << report.message << "\n"; + return 1; + } + if (plan.ckeClears.size() != 2 || report.ckeClearCount != 2) { + std::cerr << "expected local and remote notify CKE clears, got " << + plan.ckeClears.size() << " report=" << report.ckeClearCount << "\n"; + return 2; + } + if (plan.ckeClears[0].startCkeId != 0x220 || plan.ckeClears[0].count != 3) { + std::cerr << "local CKE clear mismatch\n"; + return 3; + } + if (plan.ckeClears[1].startCkeId != 0x360 || plan.ckeClears[1].count != 2) { + std::cerr << "remote notify CKE clear mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_builds_transport_template_from_basic_info_and_resource_allocation(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0; + basic.msidToken.valid = true; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (5U << 16) | 255U; + basic.caps.cap1 = (127U << 16) | 63U; + basic.caps.cap2 = (31U << 16) | 15U; + basic.caps.cap3 = (7U << 16) | 1U; + basic.caps.cap4 = 9U; + + TileXRCcuSpecInfo specInfo; + TileXRCcuSpecsReport specsReport; + if (TileXRCcuDecodeBasicInfo(basic, &specInfo, &specsReport) != TILEXR_SUCCESS) { + std::cerr << "decode failed: " << specsReport.message << "\n"; + return 1; + } + + TileXRCcuResourceSpec resourceSpec; + if (TileXRCcuBuildResourceSpec(specInfo, 6, 475, 1961, 332, 2, &resourceSpec, &specsReport) != + TILEXR_SUCCESS) { + std::cerr << "resource spec failed: " << specsReport.message << "\n"; + return 2; + } + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(resourceSpec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 3; + } + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 2; + request.syncInstructionCount = 9; + request.bindingsPerSyncResource = 1; + TileXRCcuProducerPlan producerPlan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport allocatorReport; + if (allocator.Allocate(request, &producerPlan, &allocation, &allocatorReport) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << allocatorReport.message << "\n"; + return 4; + } + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = specInfo.xnBaseAddr + allocation.remoteXn.startId * 8ULL; + remote0.memoryTokenId = 0x23456; + remote0.memoryTokenValue = 0x5678; + remote0.remoteNotifyCke = 0x360; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = specInfo.xnBaseAddr + (allocation.remoteXn.startId + 1U) * 8ULL; + remote1.memoryTokenId = 0x23457; + remote1.memoryTokenValue = 0x5679; + remote1.remoteNotifyCke = 0x361; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 5; + } + if (!snapshot.msidToken.valid || snapshot.msidToken.dieId != 1 || + snapshot.msidToken.msId != 0x45 || snapshot.msidToken.tokenId != 0x1234 || + snapshot.msidToken.tokenValue != 0) { + std::cerr << "msid token template mismatch\n"; + return 6; + } + if (snapshot.dieId != 1 || snapshot.pfeOffset != allocation.channels.startId || + snapshot.pfeId != allocation.channels.startId || + snapshot.startJettyId != 1024 || snapshot.startLocalJettyCtxId != 0 || + snapshot.xnStartId != allocation.localXn.startId || + snapshot.xnCount != allocation.localXn.num || + snapshot.ckeStartId != allocation.notifyCke.startId || + snapshot.ckeCount != allocation.notifyCke.num) { + std::cerr << "template scalar mismatch\n"; + return 7; + } + if (snapshot.routes.size() != 2 || snapshot.routes[0].channelId != allocation.channels.startId || + snapshot.routes[1].channelId != allocation.channels.startId + 1U || + snapshot.routes[0].remoteXnId != allocation.remoteXn.startId || + snapshot.routes[1].remoteXnId != allocation.remoteXn.startId + 1U || + snapshot.routes[0].remoteNotifyCke != 0x360 || + snapshot.routes[1].remoteNotifyCke != 0x361 || + snapshot.routes[0].wqeBasicBlockStartId != 0 || + snapshot.routes[1].wqeBasicBlockStartId != 4 || + snapshot.routes[0].remoteCcuVa != remoteCcuBuffers[0].remoteCcuVa || + snapshot.routes[1].remoteCcuVa != remoteCcuBuffers[1].remoteCcuVa || + snapshot.routes[0].memoryTokenId != 0x23456 || + snapshot.routes[0].memoryTokenValue != 0x5678 || + snapshot.routes[1].memoryTokenId != 0x23457 || + snapshot.routes[1].memoryTokenValue != 0x5679) { + std::cerr << "route template mismatch\n"; + return 8; + } + if (report.message != "ok" || report.channelCount != 2 || report.ckeClearCount != 1) { + std::cerr << "report mismatch\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_accepts_basic_info_without_basic_msid_token(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0; + basic.msidToken.tokenValue = 0; + basic.msidToken.valid = false; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 1}; + allocation.localXn = {1, 0x1a0, 1}; + allocation.remoteXn = {1, 0x2a0, 1}; + allocation.notifyCke = {1, 0x220, 1}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + remote.memoryTokenValue = 0x5678; + remote.remoteNotifyCke = 0x360; + remoteCcuBuffers.push_back(remote); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.msidToken.valid || snapshot.msidToken.tokenId != 0 || + snapshot.msidToken.tokenValue != 0 || snapshot.msidToken.dieId != 1 || + snapshot.msidToken.msId != 0x45) { + std::cerr << "basic-info token should remain absent in template\n"; + return 2; + } + if (report.msidTokenCount != 0 || report.channelCount != 1 || + snapshot.routes.size() != 1 || snapshot.routes[0].memoryTokenId != 0x23456) { + std::cerr << "template route/report mismatch\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_promotes_verified_endpoint_route_from_remote_buffer_info(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 1}; + allocation.localXn = {1, 0x1a0, 1}; + allocation.remoteXn = {1, 0x2a0, 1}; + allocation.notifyCke = {1, 0x220, 1}; + + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + remote.memoryTokenValue = 0x5678; + remote.remoteXnId = 0x2a7; + remote.remoteNotifyCke = 0x361; + for (uint32_t i = 0; i < remote.remoteEid.size(); ++i) { + remote.remoteEid[i] = static_cast(0x80 + i); + } + remote.tpn = 0x010203; + remote.doorbellVa = 0x1122334455667788ULL; + remote.doorbellTokenId = 0x3456; + remote.doorbellTokenValue = 0; + remote.sqDepth = 64; + remote.endpointRouteVerified = true; + std::vector remoteCcuBuffers {remote}; + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.routes.size() != 1 || + !snapshot.routes[0].endpointRouteVerified || + snapshot.routes[0].remoteEid[0] != 0x80 || + snapshot.routes[0].tpn != remote.tpn || + snapshot.routes[0].doorbellVa != remote.doorbellVa || + snapshot.routes[0].doorbellTokenId != remote.doorbellTokenId || + snapshot.routes[0].doorbellTokenValue != remote.doorbellTokenValue || + snapshot.routes[0].sqDepth != remote.sqDepth || + snapshot.routes[0].remoteXnId != remote.remoteXnId || + snapshot.routes[0].remoteNotifyCke != remote.remoteNotifyCke) { + std::cerr << "verified endpoint route was not promoted from remote buffer info\n"; + return 2; + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "install plan build failed: " << report.message << "\n"; + return 3; + } + if (plan.remoteXnBindings.size() != 1 || + !plan.remoteXnBindings[0].endpointRouteVerified || + plan.remoteXnBindings[0].remoteXn != remote.remoteXnId || + plan.remoteXnBindings[0].notifyCke != remote.remoteNotifyCke) { + std::cerr << "verified endpoint proof was not propagated\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_rejects_incomplete_verified_endpoint_route_from_remote_buffer_info(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 1}; + allocation.localXn = {1, 0x1a0, 1}; + allocation.remoteXn = {1, 0x2a0, 1}; + allocation.notifyCke = {1, 0x220, 1}; + + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + remote.memoryTokenValue = 0x5678; + remote.remoteXnId = 0x2a7; + remote.remoteNotifyCke = 0x361; + remote.remoteEid[0] = 0x80; + remote.tpn = 0x010203; + remote.doorbellVa = 0x1122334455667788ULL; + remote.doorbellTokenId = 0; + remote.sqDepth = 64; + remote.endpointRouteVerified = true; + std::vector remoteCcuBuffers {remote}; + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.routes.size() != 1 || + snapshot.routes[0].endpointRouteVerified || + snapshot.routes[0].remoteEid[0] != 0 || + snapshot.routes[0].tpn != 0 || + snapshot.routes[0].doorbellVa != 0 || + snapshot.routes[0].sqDepth != 0) { + std::cerr << "incomplete endpoint route should fail closed\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_rejects_channel_allocation_smaller_than_routes(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 1}; + allocation.localXn = {1, 0x1a0, 2}; + allocation.remoteXn = {1, 0x2a0, 2}; + allocation.notifyCke = {1, 0x360, 2}; + allocation.localWaitCke = {1, 0x220, 2}; + allocation.remoteNotifyCke = {1, 0x360, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remote0.memoryTokenValue = 0x5678; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "template accepted an allocation with too few channels\n"; + return 1; + } + if (report.message.find("channel allocation count") == std::string::npos) { + std::cerr << "weak channel allocation diagnostic: " << report.message << "\n"; + return 2; + } + if (!snapshot.routes.empty()) { + std::cerr << "failed template should not retain routes\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_uses_local_wait_cke_for_clear_and_remote_notify_cke_for_routes(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.tokenValue = 0; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 2}; + allocation.localXn = {1, 0x1a0, 2}; + allocation.remoteXn = {1, 0x2a0, 2}; + allocation.notifyCke = {1, 0x360, 2}; + allocation.localWaitCke = {1, 0x220, 2}; + allocation.remoteNotifyCke = {1, 0x360, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remote0.memoryTokenValue = 0x5678; + remote0.remoteNotifyCke = 0; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remote1.memoryTokenValue = 0x5679; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.ckeStartId != 0x220 || snapshot.ckeCount != 2) { + std::cerr << "local wait CKE clear range mismatch\n"; + return 2; + } + if (snapshot.routes.size() != 2 || + snapshot.routes[0].remoteNotifyCke != 0x360 || + snapshot.routes[1].remoteNotifyCke != 0x361) { + std::cerr << "remote notify CKE route fallback mismatch\n"; + return 3; + } + for (uint32_t i = 0; i < snapshot.routes.size(); ++i) { + for (uint32_t eidIndex = 0; eidIndex < TILEXR_CCU_EID_BYTES; ++eidIndex) { + snapshot.routes[i].remoteEid[eidIndex] = + static_cast(0x40 + i * 0x10 + eidIndex); + } + snapshot.routes[i].tpn = 0x010200 + i; + snapshot.routes[i].doorbellVa = 0x1111222233334444ULL + i * 0x1000ULL; + snapshot.routes[i].doorbellTokenId = 0x12345; + snapshot.routes[i].doorbellTokenValue = 0; + snapshot.routes[i].sqDepth = 8; + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "install plan build failed: " << report.message << "\n"; + return 4; + } + if (plan.remoteXnBindings.size() != 2 || + plan.remoteXnBindings[0].notifyCke != 0x360 || + plan.remoteXnBindings[0].localWaitCke != 0x220 || + plan.remoteXnBindings[1].notifyCke != 0x361 || + plan.remoteXnBindings[1].localWaitCke != 0x221) { + std::cerr << "split CKE remote XN proof mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_overlay_verified_endpoint_routes_updates_only_matching_endpoint_fields(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuLowerLayerTransportSnapshot snapshot; + snapshot.routes.resize(2); + snapshot.routes[0].channelId = 0x20; + snapshot.routes[0].peerRank = 3; + snapshot.routes[0].remoteXnId = 0x1a0; + snapshot.routes[0].remoteNotifyCke = 0x360; + snapshot.routes[0].wqeBasicBlockStartId = 0x12; + snapshot.routes[0].memoryTokenId = 0x2001; + snapshot.routes[0].remoteCcuVa = 0x90000000ULL; + snapshot.routes[1].channelId = 0x21; + snapshot.routes[1].peerRank = 4; + snapshot.routes[1].remoteXnId = 0x1a1; + snapshot.routes[1].remoteNotifyCke = 0x361; + + TileXRCcuLowerLayerTransportRoute verified; + verified.channelId = 0x20; + verified.peerRank = 99; + verified.remoteXnId = 0x2b0; + verified.remoteNotifyCke = 0x470; + for (uint32_t i = 0; i < verified.remoteEid.size(); ++i) { + verified.remoteEid[i] = static_cast(0xc0 + i); + } + verified.tpn = 0x010203; + verified.doorbellVa = 0x1122334455667788ULL; + verified.doorbellTokenId = 0x3456; + verified.doorbellTokenValue = 0; + verified.sqDepth = 64; + verified.wqeBasicBlockStartId = 0x77; + verified.memoryTokenId = 0x9999; + verified.remoteCcuVa = 0xabcdefULL; + verified.endpointRouteVerified = true; + + TileXRCcuLowerLayerPlanBuilderReport report; + std::vector routes {verified}; + if (TileXRCcuOverlayVerifiedEndpointRoutes(routes, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "overlay failed: " << report.message << "\n"; + return 1; + } + + const auto& route0 = snapshot.routes[0]; + if (!route0.endpointRouteVerified || + route0.remoteEid[0] != 0xc0 || + route0.tpn != verified.tpn || + route0.doorbellVa != verified.doorbellVa || + route0.doorbellTokenId != verified.doorbellTokenId || + route0.doorbellTokenValue != verified.doorbellTokenValue || + route0.sqDepth != verified.sqDepth) { + std::cerr << "verified endpoint fields were not overlaid\n"; + return 2; + } + if (route0.peerRank != 3 || + route0.remoteXnId != 0x1a0 || + route0.remoteNotifyCke != 0x360 || + route0.wqeBasicBlockStartId != 0x12 || + route0.memoryTokenId != 0x2001 || + route0.remoteCcuVa != 0x90000000ULL) { + std::cerr << "overlay changed non-endpoint resource fields\n"; + return 3; + } + if (snapshot.routes[1].endpointRouteVerified || + snapshot.routes[1].remoteEid[0] != 0 || + snapshot.routes[1].tpn != 0 || + snapshot.routes[1].doorbellVa != 0) { + std::cerr << "overlay changed an unmatched route\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_uses_peer_exchanged_remote_xn_ids_when_present(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 2}; + allocation.localXn = {1, 0x2a0, 2}; + allocation.remoteXn = {1, 0x2b0, 2}; + allocation.notifyCke = {1, 0x360, 2}; + allocation.localWaitCke = {1, 0x220, 2}; + allocation.remoteNotifyCke = {1, 0x360, 2}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote0; + remote0.remoteCcuVa = 0x0000009234000000ULL; + remote0.memoryTokenId = 0x23456; + remote0.memoryTokenValue = 0x5678; + remote0.remoteXnId = 0x1a0; + remote0.remoteNotifyCke = 0x360; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = 0x0000009334000000ULL; + remote1.memoryTokenId = 0x23457; + remote1.memoryTokenValue = 0x5679; + remote1.remoteXnId = 0x1a1; + remote1.remoteNotifyCke = 0x361; + remoteCcuBuffers.push_back(remote1); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + if (snapshot.routes.size() != 2 || + snapshot.routes[0].remoteXnId != 0x1a0 || + snapshot.routes[1].remoteXnId != 0x1a1) { + std::cerr << "peer exchanged remote XN IDs were not preserved\n"; + return 2; + } + for (uint32_t i = 0; i < snapshot.routes.size(); ++i) { + for (uint32_t eidIndex = 0; eidIndex < TILEXR_CCU_EID_BYTES; ++eidIndex) { + snapshot.routes[i].remoteEid[eidIndex] = + static_cast(0x40 + i * 0x10 + eidIndex); + } + snapshot.routes[i].tpn = 0x010200 + i; + snapshot.routes[i].doorbellVa = 0x1111222233334444ULL + i * 0x1000ULL; + snapshot.routes[i].doorbellTokenId = 0x12345; + snapshot.routes[i].sqDepth = 8; + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "install plan build failed: " << report.message << "\n"; + return 3; + } + if (plan.remoteXnBindings.size() != 2 || + plan.remoteXnBindings[0].remoteXn != 0x1a0 || + plan.remoteXnBindings[1].remoteXn != 0x1a1 || + !plan.remoteXnBindings[0].peerExchangeObserved || + !plan.remoteXnBindings[1].peerExchangeObserved || + plan.remoteXnBindings[0].endpointRouteVerified || + plan.remoteXnBindings[1].endpointRouteVerified) { + std::cerr << "peer exchanged remote XN proof mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_transport_template_carries_explicit_channel_owner_exchange_proof(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 1}; + allocation.localXn = {1, 0x2a0, 1}; + allocation.remoteXn = {1, 0x2b0, 1}; + allocation.notifyCke = {1, 0x360, 1}; + allocation.localWaitCke = {1, 0x220, 1}; + allocation.remoteNotifyCke = {1, 0x360, 1}; + + TileXRCcuRemoteCcuBufferInfo endpointOnly; + endpointOnly.remoteCcuVa = 0x0000009234000000ULL; + endpointOnly.memoryTokenId = 0x23456; + endpointOnly.memoryTokenValue = 0x5678; + endpointOnly.remoteXnId = 0x1a0; + endpointOnly.remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + endpointOnly.remoteEid[i] = static_cast(0x40 + i); + } + endpointOnly.tpn = 0x010200; + endpointOnly.doorbellVa = 0x1111222233334444ULL; + endpointOnly.doorbellTokenId = 0x12345; + endpointOnly.sqDepth = 8; + endpointOnly.endpointRouteVerified = true; + + TileXRCcuLowerLayerTransportSnapshot endpointOnlySnapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, {endpointOnly}, &endpointOnlySnapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "endpoint-only template build failed: " << report.message << "\n"; + return 1; + } + TileXRCcuLowerLayerInstallPlan endpointOnlyPlan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot( + endpointOnlySnapshot, &endpointOnlyPlan, &report) != TILEXR_SUCCESS) { + std::cerr << "endpoint-only plan build failed: " << report.message << "\n"; + return 2; + } + if (!endpointOnlyPlan.remoteXnBindings[0].endpointRouteVerified || + endpointOnlyPlan.remoteXnBindings[0].channelResourceOwnerVerified || + endpointOnlyPlan.remoteXnBindings[0].transportResourceExchangeVerified) { + std::cerr << "endpoint route alone was promoted to owner/exchange proof\n"; + return 3; + } + + TileXRCcuRemoteCcuBufferInfo proven = endpointOnly; + proven.channelResourceOwnerVerified = true; + proven.transportResourceExchangeVerified = true; + + TileXRCcuLowerLayerTransportSnapshot provenSnapshot; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, {proven}, &provenSnapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "proven template build failed: " << report.message << "\n"; + return 4; + } + if (!provenSnapshot.routes[0].channelResourceOwnerVerified || + !provenSnapshot.routes[0].transportResourceExchangeVerified) { + std::cerr << "route did not preserve owner/exchange proof\n"; + return 5; + } + + TileXRCcuLowerLayerInstallPlan provenPlan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot( + provenSnapshot, &provenPlan, &report) != TILEXR_SUCCESS) { + std::cerr << "proven plan build failed: " << report.message << "\n"; + return 6; + } + const auto& proof = provenPlan.remoteXnBindings[0]; + if (!proof.endpointRouteVerified || + !proof.channelResourceOwnerVerified || + !proof.transportResourceExchangeVerified) { + std::cerr << "install proof did not preserve owner/exchange proof\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_plan_builder_rejects_incomplete_lower_layer_inputs(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuLowerLayerInstallPlan plan; + TileXRCcuLowerLayerPlanBuilderReport report; + TileXRCcuLowerLayerPlanSpec spec; + if (TileXRCcuBuildLowerLayerInstallPlan(spec, &plan, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty lower-layer spec accepted\n"; + return 1; + } + if (report.message.find("missing lower-layer CCU MSID token") == std::string::npos) { + std::cerr << "weak empty-spec diagnostic: " << report.message << "\n"; + return 2; + } + + spec.msidToken.valid = true; + spec.msidToken.tokenId = 0; + spec.pfe.dieId = 0; + spec.pfe.pfeOffset = 1; + spec.pfe.startJettyId = 0x10; + spec.pfe.startLocalJettyCtxId = 0x2; + TileXRCcuLowerLayerJettySpec jetty; + jetty.startJettyCtxId = 0x2; + jetty.doorbellVa = 0x1000; + jetty.doorbellTokenId = 9; + jetty.sqDepth = 8; + spec.jettys.push_back(jetty); + TileXRCcuLowerLayerChannelSpec channel; + channel.channelId = 1; + channel.sourcePfeId = 1; + channel.startJettyId = 0x10; + channel.memoryTokenId = 7; + channel.remoteCcuVa = 0x200000; + spec.channels.push_back(channel); + spec.xnClear.valid = true; + spec.xnClear.count = 1; + spec.ckeClear.valid = true; + spec.ckeClear.count = 1; + if (TileXRCcuBuildLowerLayerInstallPlan(spec, &plan, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "channel with empty remote EID accepted\n"; + return 3; + } + if (report.message.find("invalid CCU channel context v1 spec") == std::string::npos) { + std::cerr << "weak channel diagnostic: " << report.message << "\n"; + return 4; + } + + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + spec.channels[0].remoteEid[i] = static_cast(0x40 + i); + } + if (TileXRCcuBuildLowerLayerInstallPlan(spec, &plan, &report) != TILEXR_SUCCESS) { + std::cerr << "valid zero-token lower-layer spec rejected: " << report.message << "\n"; + return 5; + } + if (plan.msidTokens.empty() || plan.msidTokens[0].tokenId != 0 || + plan.channels.empty() || plan.channels[0].ctx.raw[0] != 0x40) { + std::cerr << "valid zero-token lower-layer plan mismatch\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_plan_builder_surface_is_wired_into_tilexr_comm_without_udma_boundary(self): + header = BUILDER_HEADER.read_text(encoding="utf-8") + source = BUILDER_SOURCE.read_text(encoding="utf-8") + runtime_header = DIRECT_RUNTIME_HEADER.read_text(encoding="utf-8") + runtime_source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TileXRCcuLowerLayerTransportSnapshot", header) + self.assertIn("TileXRCcuLowerLayerTransportRoute", header) + self.assertIn("TileXRCcuRemoteCcuBufferInfo", header) + self.assertIn("TileXRCcuBuildLowerLayerTransportTemplate", header) + self.assertIn("TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot", header) + self.assertIn("remoteXnBindings", header) + self.assertIn("localWaitCke", source) + self.assertIn("TileXRCcuBuildPfeCtx", source) + self.assertIn("TileXRCcuBuildLocalJettyCtx", source) + self.assertIn("TileXRCcuBuildChannelCtxV1", source) + self.assertIn("allocation.channels.num < remoteCcuBuffers.size()", source) + self.assertIn("channel allocation count does not match lower-layer route count", source) + self.assertNotIn("TILEXR_CCU_DIRECT_SYNC_RESOURCE_MAP", source) + self.assertNotIn("UseHcommTraceSyncResourceMap", source) + self.assertIn("SelectLowerLayerWqeBasicBlockStride", source) + self.assertIn("TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE", source) + self.assertIn("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE", source) + self.assertIn("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION", source) + + self.assertIn("TileXRCcuDirectRuntime", runtime_header) + self.assertIn("ExportLowerLayerTransportSnapshot", runtime_header) + self.assertIn("RegisterCcuResourceRmaBuffer", runtime_header) + self.assertIn("ExportRemoteCcuRmaBuffers", runtime_header) + self.assertIn("TileXRCcuLocalResourceWindowInfo", runtime_header) + self.assertIn("TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot", runtime_source) + self.assertIn("TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_EXCHANGE_MODE", runtime_source) + self.assertIn("UseImportedPeerEndpointRoute", runtime_source) + self.assertIn("useImportedPeerRoute &&", runtime_source) + self.assertIn("usePeerExportedRoute", runtime_source) + self.assertRegex( + runtime_source, + r"(?s)\} else \{\s*remote\.tpn = peerWindow\.tpn;.*?usePeerExportedRoute", + ) + self.assertNotIn( + "} else if (!useImportedPeerRoute && TraceEndpointRoute() && peerWindow.endpointRouteVerified)", + runtime_source, + ) + self.assertNotIn("udma/", runtime_header + "\n" + runtime_source) + + combined = header + "\n" + source + "\n" + runtime_header + "\n" + runtime_source + for needle in [ + "#include &remoteCcuBuffers", comm_source) + self.assertIn("TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot", comm_source) + self.assertIn("ccuDirectRuntime_->RegisterCcuResourceRmaBuffer(directCcuBasicInfo_.resourceAddr)", comm_source) + self.assertIn("ccuDirectRuntime_->ExportRemoteCcuRmaBuffers", comm_source) + self.assertIn("ccuDirectRuntime_->ExportLowerLayerTransportSnapshot", comm_source) + self.assertIn("RefreshDirectCcuLowerLayerPlan();", comm_source) + self.assertIn("direct CCU lower-layer template is not configured", comm_source) + self.assertIn("direct CCU lower-layer install plan cached", comm_source) + + register_body = comm_source[ + comm_source.index("int TileXRComm::RegisterUDMAMemory"): + comm_source.index("int TileXRComm::UnregisterUDMAMemory") + ] + self.assertIn("ret = UpdateCommArgsDev();", register_body) + self.assertNotIn("RefreshDirectCcuLowerLayerPlan();", register_body) + self.assertNotIn("ResetDirectCcuLowerLayerPlan();", register_body) + + unregister_body = comm_source[ + comm_source.index("int TileXRComm::UnregisterUDMAMemory"): + comm_source.index("GM_ADDR TileXRComm::GetUDMARegistryPtr") + ] + self.assertNotIn("ResetDirectCcuLowerLayerPlan();", unregister_body) + + init_udma_body = comm_source[ + comm_source.index("int TileXRComm::InitUDMA"): + comm_source.index("int TileXRComm::InitDirectCcuRuntime") + ] + self.assertNotIn("RefreshDirectCcuBasicInfo", init_udma_body) + self.assertNotIn("ResetDirectCcuBasicInfo", init_udma_body) + + forbidden_patterns = [ + ("udmaTransport_->", "RegisterCcuResourceRmaBuffer"), + ("udmaTransport_->", "ExportLocalCcuRmaBuffer"), + ("udmaTransport_->", "ExportRemoteCcuRmaBuffers"), + ("udmaTransport_->", "ExportLowerLayerTransportSnapshot"), + ] + for prefix, suffix in forbidden_patterns: + self.assertNotIn(prefix + suffix, comm_header + "\n" + comm_source) + for forbidden in [ + "rtCCULaunch", + "TileXRCcuSubmitTask", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "libhcomm", + "libhccl_v2", + ]: + self.assertNotIn(forbidden, comm_header + "\n" + comm_source) + + def test_direct_ccu_runtime_owns_resource_window_boundary(self): + runtime_header = DIRECT_RUNTIME_HEADER.read_text(encoding="utf-8") + runtime_source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + specs_header = (REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.h").read_text(encoding="utf-8") + comm_header = COMM_HEADER_FILE.read_text(encoding="utf-8") + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + builder_header = BUILDER_HEADER.read_text(encoding="utf-8") + + self.assertIn("TileXRCcuLocalResourceWindowInfo", runtime_header) + self.assertIn("rawTokenId", runtime_header) + self.assertIn("rawMemoryTokenId", builder_header) + self.assertIn("TileXRCcuDirectAllGatherFn", runtime_header) + self.assertIn("localResourceWindow_", runtime_header) + self.assertIn("resourceWindowRegistered_", runtime_header) + self.assertIn("int RegisterCcuResourceRmaBuffer(", runtime_header) + self.assertIn("ExportLocalCcuRmaBuffer", runtime_header) + self.assertIn("int ExportRemoteCcuRmaBuffers(", runtime_header) + self.assertIn("int TileXRCcuDirectRuntime::RegisterCcuResourceRmaBuffer", runtime_source) + self.assertIn("int TileXRCcuDirectRuntime::ExportLocalCcuRmaBuffer", runtime_source) + self.assertIn("int TileXRCcuDirectRuntime::ExportRemoteCcuRmaBuffers", runtime_source) + self.assertIn("int TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot", runtime_source) + self.assertIn("localResourceWindow_.addr = resourceAddr", runtime_source) + self.assertIn("resourceWindowRegistered_ = true", runtime_source) + self.assertIn("TILEXR_CCU_RESOURCE_WINDOW_BYTES", specs_header) + self.assertIn("72ULL * 1024ULL * 1024ULL", specs_header) + self.assertIn("return TILEXR_CCU_RESOURCE_WINDOW_BYTES", runtime_source) + self.assertIn("options_.allGather", runtime_source) + self.assertIn("route.remoteEid", runtime_source) + self.assertIn("route.doorbellVa", runtime_source) + self.assertIn("route.sqDepth", runtime_source) + + self.assertIn("ConfigureDirectCcuLowerLayerTemplateFromAllocation(", comm_header) + self.assertIn("const std::vector &remoteCcuBuffers", comm_header) + self.assertIn("PrepareDirectCcuLowerLayerTemplateFromAllocation", comm_header) + self.assertIn("ExchangeDirectCcuRemoteNotifyCke", comm_header) + self.assertIn("DirectCcuAllGatherCallback", comm_header) + self.assertIn("DirectCcuThreadAllGather", comm_header) + self.assertIn("ccuDirectRuntime_->RegisterCcuResourceRmaBuffer", comm_source) + self.assertIn("ccuDirectRuntime_->ExportLocalCcuRmaBuffer", comm_source) + self.assertIn("ccuDirectRuntime_->ExportRemoteCcuRmaBuffers", comm_source) + self.assertIn("options.allGather = &TileXRComm::DirectCcuAllGatherCallback", comm_source) + self.assertIn("options.allGatherUserData = this", comm_source) + self.assertIn("int TileXRComm::ExchangeDirectCcuRemoteNotifyCke", comm_source) + self.assertIn("int TileXRComm::DirectCcuThreadAllGather", comm_source) + self.assertIn("DirectCcuAllGatherCallback(&local, sizeof(local), all.data(), this)", comm_source) + self.assertIn("comm->DirectCcuThreadAllGather(sendBuf, sendBytes, recvBuf)", comm_source) + self.assertIn("InitThread", comm_source) + self.assertIn("ret = InitDirectCcuRuntime();", comm_source) + exchange_body = comm_source[ + comm_source.index("int TileXRComm::ExchangeDirectCcuRemoteNotifyCke"): + comm_source.index("int TileXRComm::DirectCcuAllGatherCallback") + ] + self.assertNotIn("SelectDirectCcuRemoteBindingOverride", exchange_body) + self.assertIn("peerLocalWaitCkeOffset", exchange_body) + self.assertIn("peerResources.localWaitCkeStartId", exchange_body) + self.assertIn("peerResources.localWaitCkeCount", exchange_body) + self.assertNotIn("allocation.remoteNotifyCke.startId,\n routeIndex", exchange_body) + self.assertIn("allocation.localXn.startId", comm_source) + self.assertIn("remoteXnStartId", exchange_body) + self.assertIn("remoteXnCount", exchange_body) + self.assertNotIn("TILEXR_CCU_V1_XN_RESOURCE_OFFSET", exchange_body) + self.assertNotIn("TILEXR_CCU_XN_SLOT_BYTES", exchange_body) + self.assertIn("remoteXnId", comm_source) + self.assertIn("remoteNotifyCke", comm_source) + self.assertIn("templateSnapshot.msidToken.tokenId = localCcuResourceWindow.tokenId", comm_source) + self.assertIn("templateSnapshot.msidToken.tokenValue = localCcuResourceWindow.tokenValue", comm_source) + self.assertIn("templateSnapshot.msidToken.valid = true", comm_source) + self.assertIn("directCcuVerifiedEndpointRoutes_", comm_header) + self.assertIn("TileXRComm::ConfigureDirectCcuVerifiedEndpointRoutes", comm_source) + self.assertIn("directCcuLocalVerifiedEndpointRoute_", comm_header) + self.assertIn("TileXRComm::ConfigureDirectCcuLocalVerifiedEndpointRoute", comm_source) + self.assertIn("ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute", comm_source) + self.assertIn("ccuDirectRuntime_->RefreshLocalVerifiedEndpointRoute", comm_source) + self.assertIn("TileXRCcuLocalEndpointRouteCollectorFn", runtime_header) + self.assertIn("localEndpointRouteCollector", runtime_header) + self.assertIn("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID", runtime_source) + self.assertIn("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA", runtime_source) + self.assertIn("direct CCU local endpoint route collected", runtime_source) + self.assertIn("TileXRCcuOverlayVerifiedEndpointRoutes", builder_header) + self.assertGreaterEqual(comm_source.count("TileXRCcuOverlayVerifiedEndpointRoutes("), 3) + init_runtime_body = comm_source[ + comm_source.index("int TileXRComm::InitDirectCcuRuntime"): + comm_source.index("int TileXRComm::InitSDMA") + ] + self.assertIn("logicDevId", init_runtime_body) + self.assertIn("devicePhyId", init_runtime_body) + self.assertIn("hdcType", init_runtime_body) + self.assertIn("raInitialized", init_runtime_body) + self.assertNotIn("udma/", runtime_header + "\n" + runtime_source) + + register_body = runtime_source[ + runtime_source.index("int TileXRCcuDirectRuntime::RegisterCcuResourceRmaBuffer"): + runtime_source.index("int TileXRCcuDirectRuntime::ExportLocalCcuRmaBuffer") + ] + export_local_body = runtime_source[ + runtime_source.index("int TileXRCcuDirectRuntime::ExportLocalCcuRmaBuffer"): + runtime_source.index("int TileXRCcuDirectRuntime::ExportRemoteCcuRmaBuffers") + ] + export_remote_body = runtime_source[ + runtime_source.index("int TileXRCcuDirectRuntime::ExportRemoteCcuRmaBuffers"): + runtime_source.index("int TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot") + ] + prepare_from_allocation_body = comm_source[ + comm_source.index("int TileXRComm::PrepareDirectCcuLowerLayerTemplateFromAllocation"): + comm_source.index("int TileXRComm::FillDirectCcuLowerLayerPlanFromAllocation") + ] + export_snapshot_body = runtime_source[ + runtime_source.index("int TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot"): + ] + self.assertNotIn("int TileXRCcuDirectRuntime::RegisterCcuResourceRmaBuffer(uint64_t)\n{\n return TILEXR_ERROR_NOT_FOUND;\n}", runtime_source) + self.assertNotIn("*info = TileXRCcuLocalResourceWindowInfo{};\n return TILEXR_ERROR_NOT_FOUND;", export_local_body) + self.assertNotIn("buffers->clear();\n return TILEXR_ERROR_NOT_FOUND;", export_remote_body) + self.assertNotIn("*snapshot = TileXRCcuLowerLayerTransportSnapshot{};\n return TILEXR_ERROR_NOT_FOUND;", export_snapshot_body) + self.assertLess( + prepare_from_allocation_body.index("ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute"), + prepare_from_allocation_body.index("ccuDirectRuntime_->ExportRemoteCcuRmaBuffers"), + ) + + register_memory_body = comm_source[ + comm_source.index("int TileXRComm::RegisterUDMAMemory"): + comm_source.index("int TileXRComm::UnregisterUDMAMemory") + ] + self.assertNotIn("RefreshDirectCcuLowerLayerPlan();", register_memory_body) + self.assertNotIn("ResetDirectCcuLowerLayerPlan();", register_memory_body) + + def test_remote_xn_exchange_uses_peer_channel_local_xn_operand(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + exchange_body = comm_source[ + comm_source.index("int TileXRComm::ExchangeDirectCcuRemoteNotifyCke"): + comm_source.index("int TileXRComm::DirectCcuAllGatherCallback") + ] + compact_body = " ".join(exchange_body.split()) + + self.assertIn( + "channelBoundRemoteXnId = SelectDirectCcuChannelBoundRemoteXnId( peerResources.remoteXnStartId, peerLocalIndex, syncIndex, peerRouteCount)", + compact_body) + self.assertIn( + "peerLocalXnId = static_cast(static_cast(peerResources.localXnStartId) + peerLocalXnOffset)", + compact_body) + self.assertIn("selectedRemoteXnOffset >= peerResources.remoteXnCount", compact_body) + self.assertNotIn("SelectDirectCcuRemoteBindingOverride", compact_body) + self.assertIn("(*remoteCcuBuffers)[routeIndex].remoteXnId = channelBoundRemoteXnId", compact_body) + self.assertNotIn("(*remoteCcuBuffers)[routeIndex].remoteCcuVa +=", compact_body) + self.assertNotIn("static_cast(peerLocalXnId) * TILEXR_CCU_XN_SLOT_BYTES", compact_body) + self.assertNotIn("TILEXR_CCU_V1_XN_RESOURCE_OFFSET + static_cast(peerLocalXnId)", compact_body) + self.assertNotIn( + "uint16_t remoteXnId = static_cast(peerResources.localXnStartId + peerLocalIndex)", + compact_body) + self.assertNotIn( + "channelBoundRemoteXnId = static_cast(allocation.remoteXn.startId + routeIndex)", + compact_body) + self.assertNotIn( + "static_cast((*remoteCcuBuffers)[routeIndex].remoteXnId) * TILEXR_CCU_XN_SLOT_BYTES", + compact_body) + + def test_remote_notify_cke_comes_from_peer_exported_local_wait_cke(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + exchange_body = comm_source[ + comm_source.index("int TileXRComm::ExchangeDirectCcuRemoteNotifyCke"): + comm_source.index("int TileXRComm::DirectCcuAllGatherCallback") + ] + compact_body = " ".join(exchange_body.split()) + + self.assertIn("peerLocalWaitCkeOffset", exchange_body) + self.assertIn("peerLocalWaitCkeOffset >= peerResources.localWaitCkeCount", compact_body) + self.assertIn( + "remoteNotifyCke = static_cast(static_cast(peerResources.localWaitCkeStartId) + peerLocalWaitCkeOffset)", + compact_body) + self.assertNotIn( + "remoteNotifyCke = SelectDirectCcuRemoteNotifyCkeId( allocation.remoteNotifyCke.startId, routeIndex)", + compact_body) + self.assertIn("notifyCkeOwnerVerified", exchange_body) + self.assertIn("notifyCkeOwnerVerified &&", compact_body) + + def test_peer_xn_exchange_expands_one_peer_window_to_multiple_sync_routes(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + exchange_body = comm_source[ + comm_source.index("int TileXRComm::ExchangeDirectCcuRemoteNotifyCke"): + comm_source.index("int TileXRComm::DirectCcuAllGatherCallback") + ] + compact_body = " ".join(exchange_body.split()) + + self.assertIn("const size_t peerRouteCount = static_cast(rankSize_ - 1)", compact_body) + self.assertIn("const size_t syncRouteCount = allocation.remoteXn.num", compact_body) + self.assertIn("allocation.remoteXn.num < static_cast(rankSize_ - 1)", compact_body) + self.assertNotIn("allocation.remoteXn.num != static_cast(rankSize_ - 1)", compact_body) + self.assertIn("std::vector peerCcuBuffers = *remoteCcuBuffers", compact_body) + self.assertIn("remoteCcuBuffers->assign(syncRouteCount, TileXRCcuRemoteCcuBufferInfo{})", compact_body) + self.assertIn("for (uint32_t syncIndex = 0; syncIndex < allocation.remoteXn.num; ++syncIndex)", compact_body) + self.assertIn("const size_t peerBufferIndex = syncIndex % peerRouteCount", compact_body) + self.assertIn("(*remoteCcuBuffers)[routeIndex] = peerCcuBuffers[peerBufferIndex]", compact_body) + self.assertIn("channelBoundRemoteXnId = SelectDirectCcuChannelBoundRemoteXnId(", compact_body) + self.assertIn("DirectCcuRemoteXnProofSpan(allocation.remoteXn.num)", compact_body) + + def test_direct_ccu_runtime_imports_peer_endpoint_route_before_export(self): + runtime_source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + export_body = runtime_source[ + runtime_source.index("int TileXRCcuDirectRuntime::ExportRemoteCcuRmaBuffers"): + runtime_source.index("int TileXRCcuDirectRuntime::ExportLowerLayerTransportSnapshot") + ] + compact_body = " ".join(export_body.split()) + + self.assertIn("ImportPeerEndpointRoute(", runtime_source) + self.assertIn( + "if (importedPeerRoute) { remote.remoteEid = importedRoute.remoteEid; remote.tpn = importedRoute.tpn", + compact_body) + self.assertIn("} else { remote.tpn = peerWindow.tpn", compact_body) + self.assertIn("TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET", runtime_source) + self.assertIn("const uint64_t remoteCcuVaOffset = SelectRemoteCcuVaOffset()", compact_body) + self.assertIn("remote.remoteCcuVa = peerWindow.addr + remoteCcuVaOffset", compact_body) + self.assertNotIn("remote.remoteCcuVa = peerWindow.addr;", compact_body) + self.assertIn("remote.localDoorbellVa = localVerifiedEndpointRoute_.doorbellVa", compact_body) + self.assertIn("remote.localDoorbellTokenId = localVerifiedEndpointRoute_.doorbellTokenId", compact_body) + self.assertIn("remote.localDoorbellTokenValue = localVerifiedEndpointRoute_.doorbellTokenValue", compact_body) + + def test_direct_ccu_runtime_can_override_resource_window_token_from_rank_env(self): + code = textwrap.dedent( + r''' + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int FakeRaCustomChannel(TileXRCcuRaInfo, TileXRCcuCustomChannelIn*, TileXRCcuCustomChannelOut*) + { + return 0; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.loader_.RaCustomChannel = FakeRaCustomChannel; + runtime.loader_.loaded_ = true; + runtime.options_.rank = 1; + runtime.cachedBasicInfoValid_ = true; + runtime.cachedBasicInfo_.resourceAddr = 0x10000000ULL; + runtime.cachedBasicInfo_.msidToken.tokenId = 0x1234; + runtime.cachedBasicInfo_.msidToken.tokenValue = 0x5678; + runtime.cachedBasicInfo_.msidToken.valid = true; + runtime.cachedBasicInfo_.caps.cap1 = 7U << 16U; + + if (runtime.RegisterCcuResourceRmaBuffer(0x10000000ULL) != TILEXR_SUCCESS) { + std::cerr << "register failed\n"; + return 1; + } + TileXRCcuLocalResourceWindowInfo local; + if (runtime.ExportLocalCcuRmaBuffer(&local) != TILEXR_SUCCESS) { + std::cerr << "export failed\n"; + return 2; + } + if (local.tokenId != 0x2222U || + local.rawTokenId != 0x3333U || + local.tokenValue != 0x4444U) { + std::cerr << "rank override was not applied tokenId=" << local.tokenId + << " rawTokenId=" << local.rawTokenId + << " tokenValue=" << local.tokenValue << "\n"; + return 3; + } + + TileXRCcuDirectRuntime defaultRuntime; + defaultRuntime.initialized_ = true; + defaultRuntime.loader_.RaCustomChannel = FakeRaCustomChannel; + defaultRuntime.loader_.loaded_ = true; + defaultRuntime.options_.rank = 0; + defaultRuntime.cachedBasicInfoValid_ = true; + defaultRuntime.cachedBasicInfo_ = runtime.cachedBasicInfo_; + if (defaultRuntime.RegisterCcuResourceRmaBuffer(0x10000000ULL) != TILEXR_SUCCESS || + defaultRuntime.ExportLocalCcuRmaBuffer(&local) != TILEXR_SUCCESS) { + std::cerr << "default register/export failed\n"; + return 4; + } + if (local.tokenId != 0x1111U || + local.rawTokenId != 0x1111U || + local.tokenValue != 0x7777U) { + std::cerr << "common override fallback was not applied\n"; + return 5; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID"] = "0x1111" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE"] = "0x7777" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID_RANK1"] = "0x2222" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_RAW_TOKEN_ID_RANK1"] = "0x3333" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_VALUE_RANK1"] = "0x4444" + + result = self.compile_and_run( + code, + env=env, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_can_register_resource_window_with_public_ra_ctx(self): + code = textwrap.dedent( + r''' + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + constexpr uint64_t kResourceAddr = 0x10001234ULL; + constexpr uint64_t kAlignedResourceAddr = 0x10001000ULL; + constexpr uint64_t kAlignedResourceBytes = + TILEXR_CCU_RESOURCE_WINDOW_BYTES + (kResourceAddr - kAlignedResourceAddr); + + int FakeRaCustomChannel(TileXRCcuRaInfo, TileXRCcuCustomChannelIn*, TileXRCcuCustomChannelOut*) + { + return 0; + } + + int FakeRaGetDevEidInfoNum(TileXRCcuRaInfo, uint32_t* num) + { + *num = 1; + return 0; + } + + int FakeRaGetDevEidInfoList(TileXRCcuRaInfo, TileXRCcuHccpDevEidInfo* list, uint32_t* num) + { + if (list == nullptr || num == nullptr || *num != 1) { + return -1; + } + list[0].eidIndex = 3; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + list[0].eid.raw[i] = static_cast(0xa0 + i); + } + return 0; + } + + int FakeRaCtxInit(TileXRCcuHccpCtxInitCfg*, TileXRCcuHccpCtxInitAttr* attr, void** ctx) + { + if (attr == nullptr || attr->phyId != 0x55 || attr->ub.eidIndex != 3) { + return -1; + } + *ctx = reinterpret_cast(0x1000); + return 0; + } + + int FakeRaCtxDeinit(void*) + { + return 0; + } + + int FakeRaCtxTokenIdAlloc(void* ctx, TileXRCcuHccpTokenId* token, void** tokenHandle) + { + if (ctx != reinterpret_cast(0x1000)) { + return -1; + } + token->tokenId = 0x12345600U; + *tokenHandle = reinterpret_cast(0x2000); + return 0; + } + + int FakeRaCtxTokenIdFree(void*, void*) + { + return 0; + } + + int FakeRaGetSecRandom(TileXRCcuRaInfo* info, uint32_t* value) + { + if (info == nullptr || info->phyId != 0x55 || info->mode != TILEXR_CCU_NETWORK_OFFLINE) { + return -1; + } + *value = 0xabcdef01U; + return 0; + } + + int FakeRaCtxLmemRegister(void* ctx, TileXRCcuHccpMrRegInfo* mr, void** handle) + { + if (ctx != reinterpret_cast(0x1000) || mr == nullptr || + mr->in.mem.addr != kAlignedResourceAddr || mr->in.mem.size != kAlignedResourceBytes || + mr->in.ub.tokenValue != 0xabcdef01U || + mr->in.ub.tokenIdHandle != reinterpret_cast(0x2000) || + mr->in.ub.flags.bs.tokenIdValid != 1 || + mr->in.ub.flags.bs.nonPin != 1) { + return -1; + } + mr->out.ub.tokenId = 0x12345600U; + mr->out.ub.targetSegHandle = 0x4455667788ULL; + mr->out.key.size = 5; + *handle = reinterpret_cast(0x3000); + return 0; + } + + int FakeRaCtxLmemUnregister(void*, void*) + { + return 0; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.loader_.RaCustomChannel = FakeRaCustomChannel; + runtime.loader_.loaded_ = true; + runtime.loader_.RaGetDevEidInfoNum = FakeRaGetDevEidInfoNum; + runtime.loader_.RaGetDevEidInfoList = FakeRaGetDevEidInfoList; + runtime.loader_.RaCtxInit = FakeRaCtxInit; + runtime.loader_.RaCtxDeinit = FakeRaCtxDeinit; + runtime.loader_.RaCtxTokenIdAlloc = FakeRaCtxTokenIdAlloc; + runtime.loader_.RaCtxTokenIdFree = FakeRaCtxTokenIdFree; + runtime.loader_.RaGetSecRandom = FakeRaGetSecRandom; + runtime.loader_.RaCtxLmemRegister = FakeRaCtxLmemRegister; + runtime.loader_.RaCtxLmemUnregister = FakeRaCtxLmemUnregister; + runtime.devicePhyId_ = 0x55; + runtime.options_.rank = 0; + runtime.cachedBasicInfoValid_ = true; + runtime.cachedBasicInfo_.resourceAddr = kResourceAddr; + runtime.cachedBasicInfo_.msidToken.tokenId = 0x1111; + runtime.cachedBasicInfo_.msidToken.tokenValue = 0x2222; + runtime.cachedBasicInfo_.msidToken.valid = true; + runtime.cachedBasicInfo_.caps.cap1 = 7U << 16U; + + if (runtime.RegisterCcuResourceRmaBuffer(kResourceAddr) != TILEXR_SUCCESS) { + std::cerr << "ra ctx resource window register failed\n"; + return 1; + } + + TileXRCcuLocalResourceWindowInfo local; + if (runtime.ExportLocalCcuRmaBuffer(&local) != TILEXR_SUCCESS) { + std::cerr << "export failed\n"; + return 2; + } + if (local.tokenId != 0x123456U || + local.rawTokenId != 0x12345600U || + local.tokenValue != 0xabcdef01U || + local.addr != kResourceAddr || + local.bytes != TILEXR_CCU_RESOURCE_WINDOW_BYTES) { + std::cerr << "unexpected registered resource window tokenId=" << local.tokenId + << " rawTokenId=" << local.rawTokenId + << " tokenValue=" << local.tokenValue + << " bytes=" << local.bytes << "\n"; + return 3; + } + + runtime.Shutdown(); + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE"] = "ra_ctx" + + result = self.compile_and_run( + code, + env=env, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_collects_ra_ctx_endpoint_route_when_resource_window_uses_ra_ctx(self): + code = textwrap.dedent( + r''' + #include + #include + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + constexpr uint32_t kExpectedSqDepth = 8; + + int EchoLocalExchangeAsPeer(const void* sendBuf, size_t sendBytes, void* recvBuf, void*) + { + if (sendBuf == nullptr || recvBuf == nullptr || + sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + if (!local->endpointRouteVerified || + local->remoteEid[0] != 0xb0 || + local->tpn != 0x010203 || + local->doorbellVa != 0x1122334455667788ULL || + local->doorbellTokenId != 0x3456 || + local->sqDepth != kExpectedSqDepth) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = *local; + return TILEXR_SUCCESS; + } + + int FakeRaCtxCqCreate(void* ctx, TileXRCcuHccpCqInfo*, void** cqHandle) + { + if (ctx != reinterpret_cast(0x1000)) { + return -1; + } + *cqHandle = reinterpret_cast(0x2000); + return 0; + } + + int FakeRaCtxCqDestroy(void*, void*) + { + return 0; + } + + int FakeRaCtxQpCreate( + void* ctx, + TileXRCcuHccpQpCreateAttr* attr, + TileXRCcuHccpQpCreateInfo* info, + void** qpHandle) + { + if (ctx != reinterpret_cast(0x1000) || + attr == nullptr || + attr->ub.tokenIdHandle != reinterpret_cast(0x1100) || + attr->ub.tokenValue != 0xabcdef01U || + info == nullptr) { + return -1; + } + info->key.size = 4; + info->ub.dbAddr = 0x1122334455667788ULL; + info->ub.dbTokenId = 0x345600U; + *qpHandle = reinterpret_cast(0x3000); + return 0; + } + + int FakeRaCtxQpDestroy(void*) + { + return 0; + } + + int FakeRaGetTpInfoListAsync( + void* ctx, + TileXRCcuHccpGetTpCfg*, + TileXRCcuHccpTpInfo infoList[], + uint32_t* num, + void** reqHandle) + { + if (ctx != reinterpret_cast(0x1000) || + infoList == nullptr || + num == nullptr || + *num == 0) { + return -1; + } + infoList[0].tpHandle = 0x99887766ULL; + *num = 1; + *reqHandle = reinterpret_cast(0x4000); + return 0; + } + + int FakeRaGetAsyncReqResult(void* reqHandle, int* reqResult) + { + if (reqHandle != reinterpret_cast(0x4000) || reqResult == nullptr) { + return -1; + } + *reqResult = 0; + return 0; + } + + int FakeRaCtxQpImport(void* ctx, TileXRCcuHccpQpImportInfo* info, void** remoteQpHandle) + { + if (ctx != reinterpret_cast(0x1000) || + info == nullptr || + info->in.ub.expImportCfg.tpHandle != 0x99887766ULL) { + return -1; + } + info->out.ub.tpn = 0x010203; + *remoteQpHandle = reinterpret_cast(0x5000); + return 0; + } + + int FakeRaCtxQpUnimport(void*, void*) + { + return 0; + } + + int main() + { + unsetenv("TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE"); + unsetenv("TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_ID"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_VALUE"); + unsetenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_SQ_DEPTH"); + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = EchoLocalExchangeAsPeer; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = TILEXR_CCU_RESOURCE_WINDOW_BYTES; + runtime.localResourceWindow_.tokenId = 0x123456U; + runtime.localResourceWindow_.rawTokenId = 0x12345600U; + runtime.localResourceWindow_.tokenValue = 0xabcdef01U; + runtime.localResourceWindow_.raCtxHandle = reinterpret_cast(0x1000); + runtime.localResourceWindow_.tokenIdHandle = reinterpret_cast(0x1100); + runtime.localResourceWindow_.raCtxRegistered = true; + for (uint32_t i = 0; i < runtime.localResourceWindow_.eid.size(); ++i) { + runtime.localResourceWindow_.eid[i] = static_cast(0xb0 + i); + } + runtime.loader_.RaCtxCqCreate = FakeRaCtxCqCreate; + runtime.loader_.RaCtxCqDestroy = FakeRaCtxCqDestroy; + runtime.loader_.RaCtxQpCreate = FakeRaCtxQpCreate; + runtime.loader_.RaCtxQpDestroy = FakeRaCtxQpDestroy; + runtime.loader_.RaCtxQpImport = FakeRaCtxQpImport; + runtime.loader_.RaCtxQpUnimport = FakeRaCtxQpUnimport; + runtime.loader_.RaGetTpInfoListAsync = FakeRaGetTpInfoListAsync; + runtime.loader_.RaGetAsyncReqResult = FakeRaGetAsyncReqResult; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_SUCCESS) { + std::cerr << "ra ctx endpoint route was not collected by default: " + << report.message << "\n"; + return 1; + } + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed after ra ctx route collection\n"; + return 2; + } + if (buffers.size() != 1 || + !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0xb0 || + buffers[0].tpn != 0x010203 || + buffers[0].doorbellVa != 0x1122334455667788ULL || + buffers[0].doorbellTokenId != 0x3456 || + buffers[0].doorbellTokenValue != 0xabcdef01U || + buffers[0].sqDepth != kExpectedSqDepth) { + std::cerr << "ra ctx collected endpoint route was not exported\n"; + return 3; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE"] = "ra_ctx" + + result = self.compile_and_run( + code, + env=env, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_rejects_zero_resource_window_token_override(self): + code = textwrap.dedent( + r''' + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int FakeRaCustomChannel(TileXRCcuRaInfo, TileXRCcuCustomChannelIn*, TileXRCcuCustomChannelOut*) + { + return 0; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.loader_.RaCustomChannel = FakeRaCustomChannel; + runtime.loader_.loaded_ = true; + runtime.options_.rank = 0; + runtime.cachedBasicInfoValid_ = true; + runtime.cachedBasicInfo_.resourceAddr = 0x10000000ULL; + runtime.cachedBasicInfo_.msidToken.tokenId = 0x1234; + runtime.cachedBasicInfo_.msidToken.valid = true; + + if (runtime.RegisterCcuResourceRmaBuffer(0x10000000ULL) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "zero token override was accepted\n"; + return 1; + } + TileXRCcuLocalResourceWindowInfo local; + if (runtime.ExportLocalCcuRmaBuffer(&local) != TILEXR_ERROR_NOT_INITIALIZED) { + std::cerr << "resource window remained registered after invalid override\n"; + return 2; + } + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_TOKEN_ID"] = "0" + + result = self.compile_and_run( + code, + env=env, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_preserves_verified_endpoint_routes_from_template(self): + code = textwrap.dedent( + r''' + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x1234; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.localResourceWindow_.addr = 0x100000000ULL; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.tokenValue = 0x5678; + + TileXRCcuLowerLayerTransportSnapshot templ; + templ.dieId = 1; + templ.xnStartId = 1961; + TileXRCcuLowerLayerTransportRoute route; + route.peerRank = 1; + route.channelId = 9; + for (uint32_t i = 0; i < route.remoteEid.size(); ++i) { + route.remoteEid[i] = static_cast(0xa0 + i); + } + route.tpn = 0; + route.doorbellVa = 0x1122334455667788ULL; + route.doorbellTokenId = 0x2345; + route.doorbellTokenValue = 0; + route.sqDepth = 64; + route.endpointRouteVerified = true; + templ.routes.push_back(route); + + TileXRCcuLowerLayerTransportRoute synthetic = route; + synthetic.peerRank = 2; + synthetic.channelId = 10; + synthetic.remoteEid = {}; + synthetic.tpn = 0; + synthetic.doorbellVa = 0; + synthetic.doorbellTokenId = 0; + synthetic.doorbellTokenValue = 0; + synthetic.sqDepth = 0; + synthetic.endpointRouteVerified = true; + templ.routes.push_back(synthetic); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + if (runtime.ExportLowerLayerTransportSnapshot(templ, &snapshot) != TILEXR_SUCCESS) { + std::cerr << "export snapshot failed\n"; + return 4; + } + if (snapshot.routes.size() != 2 || !snapshot.routes[0].endpointRouteVerified || + snapshot.routes[0].remoteEid[0] != 0xa0 || + snapshot.routes[0].tpn != route.tpn || + snapshot.routes[0].doorbellVa != route.doorbellVa || + snapshot.routes[0].doorbellTokenId != route.doorbellTokenId || + snapshot.routes[0].doorbellTokenValue != route.doorbellTokenValue || + snapshot.routes[0].sqDepth != route.sqDepth) { + std::cerr << "verified endpoint route was not preserved\n"; + return 5; + } + if (snapshot.routes[1].endpointRouteVerified || + snapshot.routes[1].remoteEid[0] == 0 || + snapshot.routes[1].tpn == 0 || + snapshot.routes[1].doorbellVa == 0 || + snapshot.routes[1].doorbellTokenId != runtime.localResourceWindow_.tokenId || + snapshot.routes[1].doorbellTokenValue != runtime.localResourceWindow_.tokenValue || + snapshot.routes[1].sqDepth == 0) { + std::cerr << "synthetic endpoint route did not fail closed\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_can_trust_synthetic_endpoint_routes_only_with_diagnostic_env(self): + code = textwrap.dedent( + r''' + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int main() + { + setenv("TILEXR_CCU_DIRECT_TRUST_SYNTHETIC_ENDPOINT_ROUTE", "1", 1); + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x1234; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.localResourceWindow_.addr = 0x100000000ULL; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.tokenValue = 0x5678; + + TileXRCcuLowerLayerTransportSnapshot templ; + templ.dieId = 1; + templ.xnStartId = 1961; + TileXRCcuLowerLayerTransportRoute route; + route.peerRank = 1; + route.channelId = 9; + route.endpointRouteVerified = true; + templ.routes.push_back(route); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + if (runtime.ExportLowerLayerTransportSnapshot(templ, &snapshot) != TILEXR_SUCCESS) { + std::cerr << "export snapshot failed\n"; + return 1; + } + if (snapshot.routes.size() != 1 || + !snapshot.routes[0].endpointRouteVerified || + snapshot.routes[0].remoteEid[0] == 0 || + snapshot.routes[0].tpn == 0 || + snapshot.routes[0].doorbellVa == 0 || + snapshot.routes[0].doorbellTokenId != runtime.localResourceWindow_.tokenId || + snapshot.routes[0].doorbellTokenValue != runtime.localResourceWindow_.tokenValue || + snapshot.routes[0].sqDepth == 0) { + std::cerr << "synthetic endpoint route was not trusted under diagnostic env\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_exchanges_verified_endpoint_route_with_resource_window(self): + code = textwrap.dedent( + r''' + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int FakeAllGather(const void* sendBuf, size_t sendBytes, void* recvBuf, void* userData) + { + (void)sendBuf; + if (sendBytes != sizeof(TileXRCcuResourceWindowExchange) || userData == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* peer = static_cast(userData); + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = *peer; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuResourceWindowExchange peer {}; + peer.addr = 0x0000009234000000ULL; + peer.bytes = 0x2000; + peer.tokenId = 0x23456; + peer.rawTokenId = 0x33456; + peer.tokenValue = 0x5678; + for (uint32_t i = 0; i < peer.remoteEid.size(); ++i) { + peer.remoteEid[i] = static_cast(0x90 + i); + } + peer.tpn = 0x010203; + peer.doorbellVa = 0x1122334455667788ULL; + peer.doorbellTokenId = 0x3456; + peer.doorbellTokenValue = 0; + peer.sqDepth = 64; + peer.endpointRouteVerified = true; + peer.channelResourceOwnerVerified = true; + peer.transportResourceExchangeVerified = true; + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = FakeAllGather; + runtime.options_.allGatherUserData = &peer; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed\n"; + return 1; + } + if (buffers.size() != 1 || + buffers[0].remoteCcuVa != peer.addr || + buffers[0].memoryTokenId != peer.tokenId || + buffers[0].rawMemoryTokenId != peer.rawTokenId || + buffers[0].memoryTokenValue != peer.tokenValue || + !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0x9f || + buffers[0].remoteEid[15] != 0x90 || + buffers[0].tpn != peer.tpn || + buffers[0].doorbellVa != peer.doorbellVa || + buffers[0].doorbellTokenId != peer.doorbellTokenId || + buffers[0].doorbellTokenValue != peer.doorbellTokenValue || + buffers[0].sqDepth != peer.sqDepth || + !buffers[0].channelResourceOwnerVerified || + !buffers[0].transportResourceExchangeVerified) { + std::cerr << "verified endpoint route was not exchanged with resource window\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_imported_peer_route_uses_hcomm_reverse_channel_eid(self): + code = textwrap.dedent( + r''' + #include + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + struct TpExchange { + uint64_t tpHandles[128] = {}; + uint32_t psn = 0; + }; + + struct ExchangeState { + TileXRCcuResourceWindowExchange peer; + }; + + int FakeAllGather(const void* sendBuf, size_t sendBytes, void* recvBuf, void* userData) + { + if (sendBuf == nullptr || recvBuf == nullptr || userData == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + auto* state = static_cast(userData); + if (sendBytes == sizeof(TileXRCcuResourceWindowExchange)) { + const auto* local = static_cast(sendBuf); + if (!local->endpointRouteVerified || local->qpKey.size == 0) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = state->peer; + return TILEXR_SUCCESS; + } + if (sendBytes == sizeof(TpExchange)) { + const auto* local = static_cast(sendBuf); + if (local->tpHandles[1] != 0x1111222233334444ULL || local->psn != 5) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TpExchange) * 2); + out[1].tpHandles[0] = 0x5555666677778888ULL; + out[1].psn = 9; + return TILEXR_SUCCESS; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + int FakeGetTpInfoListAsync( + void* ctx, + TileXRCcuHccpGetTpCfg* cfg, + TileXRCcuHccpTpInfo* tpInfo, + uint32_t* tpInfoNum, + void** reqHandle) + { + if (ctx != reinterpret_cast(0x1000) || cfg == nullptr || + tpInfo == nullptr || tpInfoNum == nullptr || reqHandle == nullptr || + cfg->peerEid.raw[0] != 0xa0 || cfg->peerEid.raw[15] != 0xaf) { + return -1; + } + tpInfo->tpHandle = 0x1111222233334444ULL; + *tpInfoNum = 1; + *reqHandle = reinterpret_cast(0x2000); + return 0; + } + + int FakeGetAsyncReqResult(void* reqHandle, int* reqResult) + { + if (reqHandle != reinterpret_cast(0x2000) || reqResult == nullptr) { + return -1; + } + *reqResult = 0; + return 0; + } + + int FakeQpImport(void* ctx, TileXRCcuHccpQpImportInfo* info, void** remoteQpHandle) + { + if (ctx != reinterpret_cast(0x1000) || info == nullptr || remoteQpHandle == nullptr || + info->in.ub.expImportCfg.tpHandle != 0x1111222233334444ULL || + info->in.ub.expImportCfg.peerTpHandle != 0x5555666677778888ULL || + info->in.ub.expImportCfg.txPsn != 5 || + info->in.ub.expImportCfg.rxPsn != 9 || + info->in.key.size == 0) { + return -1; + } + info->out.ub.tpn = 0x47; + *remoteQpHandle = reinterpret_cast(0x3000); + return 0; + } + + int main() + { + ExchangeState state; + state.peer.addr = 0x0000009234000000ULL; + state.peer.bytes = 0x2000; + state.peer.tokenId = 0x23456; + state.peer.rawTokenId = 0x33456; + state.peer.tokenValue = 0x5678; + for (uint32_t i = 0; i < state.peer.remoteEid.size(); ++i) { + state.peer.remoteEid[i] = static_cast(0xa0 + i); + state.peer.qpKey.value[i] = static_cast(0x40 + i); + } + state.peer.qpKey.size = TILEXR_CCU_HCCP_QP_KEY_BYTES; + state.peer.endpointRouteVerified = true; + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = FakeAllGather; + runtime.options_.allGatherUserData = &state; + runtime.localResourceWindow_.raCtxHandle = reinterpret_cast(0x1000); + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + runtime.localResourceWindow_.eid[0] = 0x10; + runtime.localResourceWindow_.eid[15] = 0x1f; + runtime.loader_.RaGetTpInfoListAsync = FakeGetTpInfoListAsync; + runtime.loader_.RaGetAsyncReqResult = FakeGetAsyncReqResult; + runtime.loader_.RaCtxQpImport = FakeQpImport; + runtime.endpointPsn_ = 5; + runtime.endpointQpKeyValid_ = true; + runtime.endpointQpKey_.size = TILEXR_CCU_HCCP_QP_KEY_BYTES; + runtime.localVerifiedEndpointRouteValid_ = true; + runtime.localVerifiedEndpointRoute_.endpointRouteVerified = true; + runtime.localVerifiedEndpointRoute_.remoteEid[0] = 0x10; + runtime.localVerifiedEndpointRoute_.tpn = 0x22; + runtime.localVerifiedEndpointRoute_.doorbellVa = 0x1122334455667788ULL; + runtime.localVerifiedEndpointRoute_.doorbellTokenId = 0x3456; + runtime.localVerifiedEndpointRoute_.doorbellTokenValue = 0x4567; + runtime.localVerifiedEndpointRoute_.sqDepth = 8; + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed\n"; + return 1; + } + if (buffers.size() != 1 || buffers[0].tpn != 0x47 || + buffers[0].remoteEid[0] != 0xaf || buffers[0].remoteEid[15] != 0xa0 || + buffers[0].doorbellVa != runtime.localVerifiedEndpointRoute_.doorbellVa || + buffers[0].doorbellTokenId != runtime.localVerifiedEndpointRoute_.doorbellTokenId || + !buffers[0].endpointRouteVerified) { + std::cerr << "imported peer route did not use hcomm-style reverse EID and imported TPN" + << " size=" << buffers.size(); + if (!buffers.empty()) { + std::cerr << " tpn=0x" << std::hex << buffers[0].tpn + << " eid0=0x" << static_cast(buffers[0].remoteEid[0]) + << " eid15=0x" << static_cast(buffers[0].remoteEid[15]) + << " doorbellVa=0x" << buffers[0].doorbellVa + << " doorbellTokenId=0x" << buffers[0].doorbellTokenId + << std::dec + << " endpointRouteVerified=" << buffers[0].endpointRouteVerified; + } + std::cerr << "\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_exports_global_peer_rank_with_remote_buffer(self): + code = textwrap.dedent( + r''' + #include + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int FakeAllGather(const void* sendBuf, size_t sendBytes, void* recvBuf, void*) + { + if (sendBuf == nullptr || recvBuf == nullptr || + sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = *local; + out[1].addr = 0x20000000ULL; + out[1].bytes = 0x2000; + out[1].tokenId = 0x2222; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = FakeAllGather; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed\n"; + return 1; + } + if (buffers.size() != 1 || buffers[0].peerRank != 1) { + std::cerr << "global peer rank was not exported with remote buffer\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_exports_configured_local_verified_endpoint_route(self): + code = textwrap.dedent( + r''' + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int EchoLocalExchangeAsPeer(const void* sendBuf, size_t sendBytes, void* recvBuf, void* userData) + { + (void)userData; + if (sendBuf == nullptr || sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + if (!local->endpointRouteVerified || + local->remoteEid[0] != 0x70 || + local->tpn != 0x010203 || + local->doorbellVa != 0x1122334455667788ULL || + local->doorbellTokenId != 0x3456 || + local->sqDepth != 64) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = *local; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuLowerLayerTransportRoute localRoute; + for (uint32_t i = 0; i < localRoute.remoteEid.size(); ++i) { + localRoute.remoteEid[i] = static_cast(0x70 + i); + } + localRoute.tpn = 0x010203; + localRoute.doorbellVa = 0x1122334455667788ULL; + localRoute.doorbellTokenId = 0x3456; + localRoute.doorbellTokenValue = 0; + localRoute.sqDepth = 64; + localRoute.endpointRouteVerified = true; + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = EchoLocalExchangeAsPeer; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + + if (runtime.ConfigureLocalVerifiedEndpointRoute(localRoute) != TILEXR_SUCCESS) { + std::cerr << "failed to configure local verified endpoint route\n"; + return 1; + } + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed\n"; + return 2; + } + if (buffers.size() != 1 || + !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0x70 || + buffers[0].tpn != localRoute.tpn || + buffers[0].doorbellVa != localRoute.doorbellVa || + buffers[0].doorbellTokenId != localRoute.doorbellTokenId || + buffers[0].doorbellTokenValue != localRoute.doorbellTokenValue || + buffers[0].sqDepth != localRoute.sqDepth) { + std::cerr << "configured local verified endpoint route was not exported\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_collects_local_verified_endpoint_route_before_exchange(self): + code = textwrap.dedent( + r''' + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + struct CollectorState { + int calls = 0; + uint32_t observedDevicePhyId = 0; + uint64_t observedResourceWindow = 0; + }; + + int FakeCollector( + uint32_t devicePhyId, + const TileXRCcuLocalResourceWindowInfo& localResourceWindow, + TileXRCcuLowerLayerTransportRoute* route, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevicePhyId = devicePhyId; + state->observedResourceWindow = localResourceWindow.addr; + for (uint32_t i = 0; i < route->remoteEid.size(); ++i) { + route->remoteEid[i] = static_cast(0x80 + i); + } + route->tpn = 0x010203; + route->doorbellVa = 0x1122334455667788ULL; + route->doorbellTokenId = 0x3456; + route->doorbellTokenValue = 0; + route->sqDepth = 64; + route->endpointRouteVerified = true; + return TILEXR_SUCCESS; + } + + int EchoLocalExchangeAsPeer(const void* sendBuf, size_t sendBytes, void* recvBuf, void*) + { + if (sendBuf == nullptr || sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + if (!local->endpointRouteVerified || local->remoteEid[0] != 0x80 || + local->tpn != 0x010203 || local->doorbellVa != 0x1122334455667788ULL || + local->doorbellTokenId != 0x3456 || local->sqDepth != 64) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[1] = *local; + return TILEXR_SUCCESS; + } + + int main() + { + CollectorState state; + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x1234; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.allGather = EchoLocalExchangeAsPeer; + runtime.options_.localEndpointRouteCollector = FakeCollector; + runtime.options_.localEndpointRouteCollectorUserData = &state; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_SUCCESS) { + std::cerr << "failed to refresh local endpoint route: " << report.message << "\n"; + return 1; + } + if (state.calls != 1 || state.observedDevicePhyId != 0x1234 || + state.observedResourceWindow != runtime.localResourceWindow_.addr) { + std::cerr << "collector did not receive runtime context\n"; + return 2; + } + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed after collector refresh\n"; + return 3; + } + if (buffers.size() != 1 || !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0x80 || + buffers[0].tpn != 0x010203 || + buffers[0].doorbellVa != 0x1122334455667788ULL || + buffers[0].doorbellTokenId != 0x3456 || + buffers[0].doorbellTokenValue != 0 || + buffers[0].sqDepth != 64) { + std::cerr << "collected local endpoint route was not exported\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_endpoint_collector_failure_fails_closed(self): + code = textwrap.dedent( + r''' + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int IncompleteCollector( + uint32_t, + const TileXRCcuLocalResourceWindowInfo&, + TileXRCcuLowerLayerTransportRoute* route, + void*) + { + route->remoteEid[0] = 0x90; + route->tpn = 0; + route->doorbellVa = 0x1122334455667788ULL; + route->doorbellTokenId = 0; + route->sqDepth = 64; + route->endpointRouteVerified = true; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x1234; + runtime.options_.rank = 0; + runtime.options_.rankSize = 2; + runtime.options_.localEndpointRouteCollector = IncompleteCollector; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "incomplete collected route was accepted\n"; + return 1; + } + if (runtime.localVerifiedEndpointRouteValid_) { + std::cerr << "incomplete collected route remained verified\n"; + return 2; + } + + TileXRCcuLowerLayerTransportSnapshot templ; + templ.dieId = 1; + templ.xnStartId = 1961; + TileXRCcuLowerLayerTransportRoute route; + route.peerRank = 1; + route.channelId = 9; + templ.routes.push_back(route); + + TileXRCcuLowerLayerTransportSnapshot snapshot; + if (runtime.ExportLowerLayerTransportSnapshot(templ, &snapshot) != TILEXR_SUCCESS) { + std::cerr << "export snapshot failed\n"; + return 3; + } + if (snapshot.routes.size() != 1 || snapshot.routes[0].endpointRouteVerified || + snapshot.routes[0].remoteEid[0] == 0 || snapshot.routes[0].tpn == 0 || + snapshot.routes[0].doorbellVa == 0 || snapshot.routes[0].doorbellTokenId == 0 || + snapshot.routes[0].sqDepth == 0) { + std::cerr << "collector failure did not fail closed to synthetic unverified route\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_collects_ranked_env_local_verified_endpoint_route(self): + code = textwrap.dedent( + r''' + #include + #include + #include + #include + + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + using namespace TileXR; + + int EchoLocalExchangeAsPeer(const void* sendBuf, size_t sendBytes, void* recvBuf, void*) + { + if (sendBuf == nullptr || sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + if (!local->endpointRouteVerified || local->remoteEid[0] != 0xa0 || + local->tpn != 0x010203 || local->doorbellVa != 0x1122334455667788ULL || + local->doorbellTokenId != 0x3456 || local->doorbellTokenValue != 0 || + local->sqDepth != 64) { + return TILEXR_ERROR_NOT_FOUND; + } + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[0] = *local; + return TILEXR_SUCCESS; + } + + int main() + { + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID", "00112233445566778899aabbccddeeff", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN", "7", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA", "0x1111111111111111", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_ID", "0x1111", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_VALUE", "0x22", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_SQ_DEPTH", "8", 1); + setenv( + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID_RANK1", + "a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:ae:af", + 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN_RANK1", "0x010203", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA_RANK1", "0x1122334455667788", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_ID_RANK1", "0x3456", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_VALUE_RANK1", "0", 1); + setenv("TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_SQ_DEPTH_RANK1", "64", 1); + + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x1234; + runtime.options_.rank = 1; + runtime.options_.rankSize = 2; + runtime.options_.allGather = EchoLocalExchangeAsPeer; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0x4567; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_SUCCESS) { + std::cerr << "ranked env route was not collected: " << report.message << "\n"; + return 1; + } + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed after env route collection\n"; + return 2; + } + if (buffers.size() != 1 || !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0xa0 || + buffers[0].remoteEid[15] != 0xaf || + buffers[0].tpn != 0x010203 || + buffers[0].doorbellVa != 0x1122334455667788ULL || + buffers[0].doorbellTokenId != 0x3456 || + buffers[0].doorbellTokenValue != 0 || + buffers[0].sqDepth != 64) { + std::cerr << "ranked env route was not exported\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + ], + extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_tilexr_comm_caches_direct_ccu_basic_info_without_submit_readiness(self): + comm_header = COMM_HEADER_FILE.read_text(encoding="utf-8") + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + + self.assertIn("RefreshDirectCcuBasicInfo", comm_header) + self.assertIn("HasDirectCcuBasicInfo", comm_header) + self.assertIn("GetDirectCcuBasicInfoStatus", comm_header) + self.assertIn("GetDirectCcuBasicInfo", comm_header) + self.assertIn("GetDirectCcuBasicInfoReport", comm_header) + self.assertIn("directCcuBasicInfo_", comm_header) + self.assertIn("directCcuBasicInfoReport_", comm_header) + + self.assertIn("int TileXRComm::RefreshDirectCcuBasicInfo", comm_source) + self.assertIn("bool TileXRComm::HasDirectCcuBasicInfo", comm_source) + self.assertIn("ccuDirectRuntime_->QueryBasicInfo", comm_source) + self.assertIn("direct CCU basic info cached", comm_source) + self.assertIn("ResetDirectCcuBasicInfo", comm_source) + self.assertIn("ResetDirectCcuBasicInfo();", comm_source) + self.assertNotIn("udmaTransport_->" + "QueryCcuBasicInfo", comm_source) + + for forbidden in [ + "TileXRCcuPrepareSubmitTasks", + "TileXRCcuSubmitTask", + "rtCCULaunch", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "libhcomm", + "libhccl_v2", + ]: + self.assertNotIn(forbidden, comm_header + "\n" + comm_source) + + def test_tilexr_comm_prepares_direct_ccu_install_attempt_without_submitting(self): + comm_header = COMM_HEADER_FILE.read_text(encoding="utf-8") + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + runtime_header = DIRECT_RUNTIME_HEADER.read_text(encoding="utf-8") + runtime_source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn('ccu/tilexr_ccu_direct_orchestrator.h', comm_header) + self.assertIn('ccu/tilexr_ccu_direct_runtime.h', comm_header) + self.assertIn("PrepareDirectCcuInstallAttempt", comm_header) + self.assertIn("FillDirectCcuLowerLayerPlanFromAllocation", comm_header) + self.assertIn("PrepareDirectCcuLowerLayerPlanCallback", comm_header) + self.assertIn("int CreateDriverAdapter(", runtime_header) + + self.assertIn("int TileXRComm::PrepareDirectCcuInstallAttempt", comm_source) + self.assertIn("int TileXRComm::FillDirectCcuLowerLayerPlanFromAllocation", comm_source) + self.assertIn("int TileXRComm::PrepareDirectCcuLowerLayerPlanCallback", comm_source) + self.assertIn("ccuDirectRuntime_->CreateDriverAdapter", comm_source) + self.assertIn("TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode)", comm_source) + self.assertIn("next.lowerLayerPlan = nullptr", comm_source) + self.assertIn( + "next.prepareLowerLayerPlan = &TileXRComm::PrepareDirectCcuLowerLayerPlanCallback", + comm_source, + ) + self.assertIn("next.lowerLayerPlanUserData = this", comm_source) + self.assertIn("TileXRCcuRunDirectInstallAttempt(next, attempt, report)", comm_source) + self.assertIn("int TileXRCcuDirectRuntime::CreateDriverAdapter", runtime_source) + self.assertNotIn("udmaTransport_->" + "CreateCcuDriverAdapter", comm_source) + + combined = comm_header + "\n" + comm_source + "\n" + runtime_header + "\n" + runtime_source + for forbidden in [ + "TileXRCcuPrepareSubmitTasks", + "TileXRCcuSubmitTask", + "rtCCULaunch", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "libhcomm", + "libhccl_v2", + ]: + self.assertNotIn(forbidden, combined) + + def test_tilexr_comm_direct_ccu_prepare_fails_fast_after_process_init_failure(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + + self.assertIn("g_ccuDirectRuntimeUnavailableMessage", comm_source) + init_body = comm_source[ + comm_source.index("int TileXRComm::InitDirectCcuRuntime"): + comm_source.index("int TileXRComm::InitSDMA") + ] + prepare_body = comm_source[ + comm_source.index("int TileXRComm::PrepareDirectCcuInstallAttempt"): + comm_source.index("int TileXRComm::RefreshDirectCcuLowerLayerPlan") + ] + + self.assertIn("g_ccuDirectRuntimeUnavailableMessage = runtimeReport.message", init_body) + self.assertIn("direct CCU runtime unavailable after process-level init failure", comm_source) + self.assertIn("ProcessDirectCcuRuntimeUnavailableMessage()", prepare_body) + self.assertLess( + prepare_body.index("ProcessDirectCcuRuntimeUnavailableMessage()"), + prepare_body.index("RefreshDirectCcuBasicInfo(installDieId)"), + ) + + def test_tilexr_comm_direct_ccu_runtime_init_serializes_ra_initialization(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + init_body = comm_source[ + comm_source.index("int TileXRComm::InitDirectCcuRuntime"): + comm_source.index("int TileXRComm::InitSDMA") + ] + + lock_pos = init_body.index("lock_guard lock(g_ccuDirectRuntimeMtx);") + allocation_pos = init_body.index("ccuDirectRuntime_.reset(new (nothrow) TileXRCcuDirectRuntime())") + runtime_init_pos = init_body.index("ccuDirectRuntime_->Init(options, &runtimeReport)") + unavailable_set_pos = init_body.index("g_ccuDirectRuntimeUnavailable = true") + + self.assertLess(lock_pos, allocation_pos) + self.assertLess(allocation_pos, runtime_init_pos) + self.assertLess(runtime_init_pos, unavailable_set_pos) + self.assertEqual(1, init_body.count("lock_guard lock(g_ccuDirectRuntimeMtx);")) + + def test_tilexr_comm_direct_ccu_prepare_can_select_install_die_for_diagnostics(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + prepare_body = comm_source[ + comm_source.index("int TileXRComm::PrepareDirectCcuInstallAttempt"): + comm_source.index("int TileXRComm::RefreshDirectCcuLowerLayerPlan") + ] + + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_DIE_ID", comm_source) + self.assertIn("SelectDirectCcuInstallDieId", comm_source) + self.assertIn("RefreshDirectCcuBasicInfo(installDieId)", prepare_body) + self.assertIn("directCcuBasicInfo_.dieId != installDieId", prepare_body) + self.assertNotIn("RefreshDirectCcuBasicInfo(0)", prepare_body) + + def test_tilexr_comm_direct_ccu_thread_allgather_aborts_after_process_init_failure(self): + comm_source = COMM_SOURCE_FILE.read_text(encoding="utf-8") + thread_allgather_body = comm_source[ + comm_source.index("int TileXRComm::DirectCcuThreadAllGather"): + comm_source.index("int TileXRComm::PrepareDirectCcuLowerLayerPlanCallback") + ] + + self.assertIn("ProcessDirectCcuRuntimeUnavailableMessage()", thread_allgather_body) + self.assertIn("direct CCU thread allgather abort", thread_allgather_body) + self.assertLess( + thread_allgather_body.index("ProcessDirectCcuRuntimeUnavailableMessage()"), + thread_allgather_body.index("TILEXR_INIT_TIMEOUT"), + ) + + def test_tilexr_comm_direct_ccu_lower_layer_plan_api_is_header_visible(self): + code = textwrap.dedent( + r''' + #include "tilexr_comm.h" + + #include + + using namespace TileXR; + + int main() + { + using ConfigureFn = int (TileXRComm::*)(const TileXRCcuLowerLayerTransportSnapshot&); + using ConfigureFromAllocationFn = int (TileXRComm::*)( + const TileXRCcuResourceAllocation&, + const std::vector&); + using PrepareFromAllocationFn = int (TileXRComm::*)(const TileXRCcuResourceAllocation&); + using RefreshFn = int (TileXRComm::*)(); + using HasFn = bool (TileXRComm::*)() const; + using StatusFn = int (TileXRComm::*)() const; + using ReportFn = const TileXRCcuLowerLayerPlanBuilderReport& (TileXRComm::*)() const; + using PlanFn = const TileXRCcuLowerLayerInstallPlan* (TileXRComm::*)() const; + using BasicRefreshFn = int (TileXRComm::*)(uint8_t); + using BasicHasFn = bool (TileXRComm::*)() const; + using BasicStatusFn = int (TileXRComm::*)() const; + using BasicInfoFn = const TileXRCcuBasicInfo* (TileXRComm::*)() const; + using BasicReportFn = const TileXRCcuDriverAdapterReport& (TileXRComm::*)() const; + using ConfigureVerifiedEndpointRoutesFn = int (TileXRComm::*)( + const std::vector&); + using ConfigureLocalVerifiedEndpointRouteFn = int (TileXRComm::*)( + const TileXRCcuLowerLayerTransportRoute&); + using InstallAttemptFn = int (TileXRComm::*)( + const TileXRCcuDirectInstallOptions&, + TileXRCcuDirectInstallAttempt*, + TileXRCcuDirectInstallReport*); + + ConfigureFn configure = &TileXRComm::ConfigureDirectCcuLowerLayerTemplate; + ConfigureFromAllocationFn configureFromAllocation = + &TileXRComm::ConfigureDirectCcuLowerLayerTemplateFromAllocation; + PrepareFromAllocationFn prepareFromAllocation = + &TileXRComm::PrepareDirectCcuLowerLayerTemplateFromAllocation; + RefreshFn refresh = &TileXRComm::RefreshDirectCcuLowerLayerPlan; + HasFn has = &TileXRComm::HasDirectCcuLowerLayerPlan; + StatusFn status = &TileXRComm::GetDirectCcuLowerLayerPlanStatus; + ReportFn report = &TileXRComm::GetDirectCcuLowerLayerPlanReport; + PlanFn plan = &TileXRComm::GetDirectCcuLowerLayerPlan; + BasicRefreshFn basicRefresh = &TileXRComm::RefreshDirectCcuBasicInfo; + BasicHasFn basicHas = &TileXRComm::HasDirectCcuBasicInfo; + BasicStatusFn basicStatus = &TileXRComm::GetDirectCcuBasicInfoStatus; + BasicInfoFn basicInfo = &TileXRComm::GetDirectCcuBasicInfo; + BasicReportFn basicReport = &TileXRComm::GetDirectCcuBasicInfoReport; + ConfigureVerifiedEndpointRoutesFn configureVerifiedEndpointRoutes = + &TileXRComm::ConfigureDirectCcuVerifiedEndpointRoutes; + ConfigureLocalVerifiedEndpointRouteFn configureLocalVerifiedEndpointRoute = + &TileXRComm::ConfigureDirectCcuLocalVerifiedEndpointRoute; + InstallAttemptFn installAttempt = &TileXRComm::PrepareDirectCcuInstallAttempt; + (void)configure; + (void)configureFromAllocation; + (void)prepareFromAllocation; + (void)refresh; + (void)has; + (void)status; + (void)report; + (void)plan; + (void)basicRefresh; + (void)basicHas; + (void)basicStatus; + (void)basicInfo; + (void)basicReport; + (void)configureVerifiedEndpointRoutes; + (void)configureLocalVerifiedEndpointRoute; + (void)installAttempt; + return 0; + } + ''' + ) + + result = self.compile_only(code) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_memory_program.py b/tests/ccu/test_tilexr_ccu_memory_program.py new file mode 100644 index 0000000..29868ac --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_memory_program.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +MEMORY_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_memory_program.h" +MEMORY_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_memory_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + #include + + using namespace TileXR; + + int main() + { + if (TileXRCcuPackMemoryToken(0x12345, 0x89abcdefU, true) != + 0x0011234589abcdefULL) { + std::cerr << "packed token mismatch\n"; + return 1; + } + + TileXRCcuMemoryCopySpec spec; + spec.direction = TileXRCcuMemoryCopyDirection::LocalToRemote; + spec.localGsa = 0x101; + spec.localXn = 0x102; + spec.remoteGsa = 0x201; + spec.remoteXn = 0x202; + spec.lengthXn = 0x301; + spec.localAddr = 0x1122334455667788ULL; + spec.localToken = TileXRCcuPackMemoryToken(0x12345, 0x89abcdefU, true); + spec.remoteAddr = 0x8877665544332211ULL; + spec.remoteToken = TileXRCcuPackMemoryToken(0x23456, 0x76543210U, true); + spec.lengthBytes = 0x80; + spec.channelId = 0x12; + spec.completionCke = 0x401; + spec.completionMask = 1; + + std::vector program; + TileXRCcuMemoryProgramReport report; + if (TileXRCcuBuildMemoryCopyProgram(spec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "memory copy program build failed: " << report.message << "\n"; + return 2; + } + if (program.size() != 7 || report.loadInstructionCount != 5 || + report.transferInstructionCount != 1 || report.waitInstructionCount != 1 || + report.totalInstructionCount != 7 || report.message != "ok") { + std::cerr << "unexpected memory program report\n"; + return 3; + } + if (program[0].words[0] != 0x5566778801010002ULL || + program[0].words[1] != 0x0000000011223344ULL || + program[1].words[0] != 0x89abcdef01020003ULL || + program[1].words[1] != 0x0000000100112345ULL || + program[2].words[0] != 0x4433221102010002ULL || + program[2].words[1] != 0x0000000088776655ULL || + program[3].words[0] != 0x7654321002020003ULL || + program[3].words[1] != 0x0000000100123456ULL || + program[4].words[0] != 0x0000008003010003ULL || + program[4].words[1] != 0) { + std::cerr << "unexpected load immediate program\n"; + return 4; + } + if (program[5].words[0] != 0x0101020202011009ULL || + program[5].words[1] != 0x0000001203010102ULL || + program[5].words[2] != 0x0003000000000000ULL || + program[5].words[3] != 0x0000000000010401ULL) { + std::cerr << "unexpected loc->rmt transfer instruction\n"; + return 5; + } + if (program[6].words[0] != 0x0000000000010804ULL || + program[6].words[1] != 0x0000000000010401ULL || + program[6].words[2] != 0 || + program[6].words[3] != 0) { + std::cerr << "unexpected completion wait instruction\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_memory_program_builds_hcomm_style_rmt_to_loc_copy(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_memory_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuMemoryCopySpec spec; + spec.direction = TileXRCcuMemoryCopyDirection::RemoteToLocal; + spec.localGsa = 0x101; + spec.localXn = 0x102; + spec.remoteGsa = 0x201; + spec.remoteXn = 0x202; + spec.lengthXn = 0x301; + spec.localAddr = 0x1122334455667788ULL; + spec.localToken = TileXRCcuPackMemoryToken(0x12345, 0x89abcdefU, true); + spec.remoteAddr = 0x8877665544332211ULL; + spec.remoteToken = TileXRCcuPackMemoryToken(0x23456, 0x76543210U, true); + spec.lengthBytes = 0x80; + spec.channelId = 0x12; + spec.completionCke = 0x401; + spec.completionMask = 1; + + std::vector program; + TileXRCcuMemoryProgramReport report; + if (TileXRCcuBuildMemoryCopyProgram(spec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "memory copy program build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 7 || + program[5].words[0] != 0x0201010201011008ULL || + program[5].words[1] != 0x0000001203010202ULL || + program[5].words[2] != 0x0003000000000000ULL || + program[5].words[3] != 0x0000000000010401ULL) { + std::cerr << "unexpected rmt->loc transfer instruction\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_memory_program_rejects_missing_required_fields(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_memory_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + std::vector program(1); + TileXRCcuMemoryProgramReport report; + TileXRCcuMemoryCopySpec empty; + if (TileXRCcuBuildMemoryCopyProgram(empty, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty memory copy spec accepted\n"; + return 1; + } + if (!program.empty() || report.message.find("missing") == std::string::npos) { + std::cerr << "weak empty spec diagnostic: " << report.message << "\n"; + return 2; + } + + TileXRCcuMemoryCopySpec spec; + spec.direction = TileXRCcuMemoryCopyDirection::LocalToRemote; + spec.localGsa = 1; + spec.localXn = 2; + spec.remoteGsa = 3; + spec.remoteXn = 4; + spec.lengthXn = 5; + spec.localAddr = 0x1000; + spec.localToken = TileXRCcuPackMemoryToken(1, 2, true); + spec.remoteAddr = 0x2000; + spec.remoteToken = TileXRCcuPackMemoryToken(3, 4, true); + spec.lengthBytes = 128; + spec.channelId = 6; + spec.completionCke = 7; + spec.completionMask = 1; + + if (TileXRCcuBuildMemoryCopyProgram(spec, nullptr, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null output program accepted\n"; + return 3; + } + + spec.lengthBytes = 0; + if (TileXRCcuBuildMemoryCopyProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("length") == std::string::npos) { + std::cerr << "zero length accepted or weak diagnostic: " << report.message << "\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_memory_program_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = MEMORY_HEADER.read_text(encoding="utf-8") + source = MEMORY_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_memory_program.h", cmake) + self.assertIn("ccu/tilexr_ccu_memory_program.cpp", cmake) + self.assertIn("enum class TileXRCcuMemoryCopyDirection", header) + self.assertIn("struct TileXRCcuMemoryCopySpec", header) + self.assertIn("struct TileXRCcuMemoryProgramReport", header) + self.assertIn("TileXRCcuPackMemoryToken", header) + self.assertIn("TileXRCcuBuildMemoryCopyProgram", header) + self.assertIn("TileXRCcuEncodeLoadImdToGsa", source) + self.assertIn("TileXRCcuEncodeLoadImdToXn", source) + self.assertIn("TileXRCcuEncodeTransRmtMemToLocMem", source) + self.assertIn("TileXRCcuEncodeTransLocMemToRmtMem", source) + self.assertIn("TileXRCcuEncodeClearCke", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_microcode.py b/tests/ccu/test_tilexr_ccu_microcode.py new file mode 100644 index 0000000..bf06d5e --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_microcode.py @@ -0,0 +1,572 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +MICROCODE_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.h" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr load; + if (TileXRCcuEncodeLoadSqeArgsToX(1961, 0, &load) != TILEXR_SUCCESS) { + std::cerr << "load encode failed\n"; + return 1; + } + if (load.words[0] != 0x0000000007a90001ULL || load.words[1] != 0 || + load.words[2] != 0 || load.words[3] != 0) { + std::cerr << "unexpected load word\n"; + return 2; + } + + TileXRCcuInstr sync; + TileXRCcuSyncXnSpec spec; + spec.remoteXn = 2361; + spec.localXn = 1961; + spec.channelId = 2; + spec.notifyCke = 364; + spec.notifyMask = 1; + spec.setCkeId = 0; + spec.setCkeMask = 0; + if (TileXRCcuEncodeSyncXn(spec, &sync) != TILEXR_SUCCESS) { + std::cerr << "sync encode failed\n"; + return 3; + } + if (sync.words[0] != 0x000007a90939100dULL || + sync.words[1] != 0x00000001016c0002ULL || + sync.words[2] != 0x0001000000000000ULL || + sync.words[3] != 0x0000000000000000ULL) { + std::cerr << "unexpected sync words\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_microcode_encodes_hcomm_v1_load_immediate_to_xn_words(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr load; + if (TileXRCcuEncodeLoadImdToXn(1961, 0x1122334455667788ULL, 0, &load) != TILEXR_SUCCESS) { + std::cerr << "load immediate encode failed\n"; + return 1; + } + if (load.words[0] != 0x5566778807a90003ULL || + load.words[1] != 0x0000000011223344ULL || + load.words[2] != 0 || + load.words[3] != 0) { + std::cerr << "unexpected load immediate words\n"; + return 2; + } + + if (TileXRCcuEncodeLoadImdToXn(0, 1, 0, &load) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuEncodeLoadImdToXn(1961, 1, 0, nullptr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid load immediate arguments accepted\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_microcode_encodes_hcomm_v1_load_immediate_to_gsa_words(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr load; + if (TileXRCcuEncodeLoadImdToGsa(510, 0x1122334455667788ULL, &load) != TILEXR_SUCCESS) { + std::cerr << "load immediate to GSA encode failed\n"; + return 1; + } + if (load.words[0] != 0x5566778801fe0002ULL || + load.words[1] != 0x0000000011223344ULL || + load.words[2] != 0 || + load.words[3] != 0) { + std::cerr << "unexpected load immediate to GSA words\n"; + return 2; + } + + if (TileXRCcuEncodeLoadImdToGsa(0, 1, &load) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuEncodeLoadImdToGsa(510, 1, nullptr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid load immediate to GSA arguments accepted\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_microcode_rejects_invalid_arguments(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + if (TileXRCcuEncodeLoadSqeArgsToX(1961, 13, nullptr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null output accepted\n"; + return 1; + } + + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadSqeArgsToX(1961, TILEXR_CCU_SQE_ARGS_LEN, &instr) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "out-of-range sqe arg accepted\n"; + return 2; + } + + TileXRCcuSyncXnSpec missingRemote; + missingRemote.localXn = 1961; + missingRemote.channelId = 2; + missingRemote.notifyCke = 364; + missingRemote.notifyMask = 1; + if (TileXRCcuEncodeSyncXn(missingRemote, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing remote xn accepted\n"; + return 3; + } + + TileXRCcuSyncXnSpec missingNotify; + missingNotify.remoteXn = 2361; + missingNotify.localXn = 1961; + missingNotify.channelId = 2; + if (TileXRCcuEncodeSyncXn(missingNotify, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing notify accepted\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_syncxn_setcke_fields_match_hcomm_v1_layout(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr sync; + TileXRCcuSyncXnSpec spec; + spec.remoteXn = 2361; + spec.localXn = 1961; + spec.channelId = 2; + spec.notifyCke = 364; + spec.notifyMask = 1; + spec.setCkeId = 400; + spec.setCkeMask = 5; + spec.waitCkeId = 401; + spec.waitCkeMask = 6; + if (TileXRCcuEncodeSyncXn(spec, &sync) != TILEXR_SUCCESS) { + std::cerr << "sync encode failed\n"; + return 1; + } + if (sync.words[0] != 0x000007a90939100dULL || + sync.words[1] != 0x00000001016c0002ULL || + sync.words[2] != 0x0001000000000000ULL || + sync.words[3] != 0x0006019100050190ULL) { + std::cerr << "sync local set/wait CKE fields are not hcomm v1 packed layout\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_synccke_fields_match_hcomm_v1_layout(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr sync; + TileXRCcuSyncCkeSpec spec; + spec.remoteCke = 0x330; + spec.localCke = 0x221; + spec.localCkeMask = 1; + spec.channelId = 2; + spec.setCkeId = 0x401; + spec.setCkeMask = 2; + spec.waitCkeId = 0x402; + spec.waitCkeMask = 3; + if (TileXRCcuEncodeSyncCke(spec, &sync) != TILEXR_SUCCESS) { + std::cerr << "synccke encode failed\n"; + return 1; + } + if (sync.words[0] != 0x000102210330100bULL || + sync.words[1] != 0x0000000000000002ULL || + sync.words[2] != 0x0001000000000000ULL || + sync.words[3] != 0x0003040200020401ULL) { + std::cerr << "synccke fields are not hcomm v1 packed layout\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_set_and_clear_cke_microcode_encoders_match_hcomm_v1_layout(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuCkeSpec setSpec; + setSpec.ckeId = 332; + setSpec.mask = 3; + setSpec.waitCkeId = 364; + setSpec.waitMask = 1; + setSpec.clearWait = true; + + TileXRCcuInstr setInstr; + if (TileXRCcuEncodeSetCke(setSpec, &setInstr) != TILEXR_SUCCESS) { + std::cerr << "set cke encode failed\n"; + return 1; + } + if (setInstr.words[0] != 0x0003014c00010802ULL || + setInstr.words[1] != 0x000000000001016cULL || + setInstr.words[2] != 0 || + setInstr.words[3] != 0) { + std::cerr << "unexpected set cke words\n"; + return 2; + } + + TileXRCcuCkeSpec clearSpec; + clearSpec.ckeId = 0; + clearSpec.mask = 0; + clearSpec.waitCkeId = 364; + clearSpec.waitMask = 1; + clearSpec.clearWait = true; + + TileXRCcuInstr clearInstr; + if (TileXRCcuEncodeClearCke(clearSpec, &clearInstr) != TILEXR_SUCCESS) { + std::cerr << "clear cke encode failed\n"; + return 3; + } + if (clearInstr.words[0] != 0x0000000000010804ULL || + clearInstr.words[1] != 0x000000000001016cULL || + clearInstr.words[2] != 0 || + clearInstr.words[3] != 0) { + std::cerr << "unexpected clear cke words\n"; + return 4; + } + + TileXRCcuCkeSpec invalid; + if (TileXRCcuEncodeSetCke(invalid, &setInstr) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuEncodeClearCke(invalid, &clearInstr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty cke operation accepted\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_memory_transfer_microcode_encoders_match_hcomm_v1_layout(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuMemTransferSpec spec; + spec.localGsa = 0x101; + spec.localXn = 0x102; + spec.remoteGsa = 0x201; + spec.remoteXn = 0x202; + spec.lengthXn = 0x301; + spec.channelId = 0x12; + spec.reduceDataType = 0x0a; + spec.reduceOpCode = 0x05; + spec.setCkeId = 0x401; + spec.setCkeMask = 0x2; + spec.waitCkeId = 0x402; + spec.waitCkeMask = 0x3; + spec.clearWait = true; + spec.lengthFromXn = true; + spec.reduceEnabled = true; + + TileXRCcuInstr read; + if (TileXRCcuEncodeTransRmtMemToLocMem(spec, &read) != TILEXR_SUCCESS) { + std::cerr << "trans rmt->loc encode failed\n"; + return 1; + } + if (read.words[0] != 0x0201010201011008ULL || + read.words[1] != 0x5a00001203010202ULL || + read.words[2] != 0x0007000000000000ULL || + read.words[3] != 0x0003040200020401ULL) { + std::cerr << "unexpected trans rmt->loc words\n"; + return 2; + } + + TileXRCcuInstr write; + if (TileXRCcuEncodeTransLocMemToRmtMem(spec, &write) != TILEXR_SUCCESS) { + std::cerr << "trans loc->rmt encode failed\n"; + return 3; + } + if (write.words[0] != 0x0101020202011009ULL || + write.words[1] != 0x5a00001203010102ULL || + write.words[2] != 0x0007000000000000ULL || + write.words[3] != 0x0003040200020401ULL) { + std::cerr << "unexpected trans loc->rmt words\n"; + return 4; + } + + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_memory_transfer_microcode_rejects_missing_required_fields(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_microcode.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuInstr instr; + TileXRCcuMemTransferSpec empty; + if (TileXRCcuEncodeTransRmtMemToLocMem(empty, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuEncodeTransLocMemToRmtMem(empty, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty transfer accepted\n"; + return 1; + } + + TileXRCcuMemTransferSpec spec; + spec.localGsa = 1; + spec.localXn = 2; + spec.remoteGsa = 3; + spec.remoteXn = 4; + spec.lengthXn = 5; + spec.channelId = 6; + spec.setCkeId = 7; + spec.setCkeMask = 8; + + if (TileXRCcuEncodeTransRmtMemToLocMem(spec, nullptr) != TILEXR_ERROR_PARA_CHECK_FAIL || + TileXRCcuEncodeTransLocMemToRmtMem(spec, nullptr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null output accepted\n"; + return 2; + } + + spec.reduceDataType = 0x10; + if (TileXRCcuEncodeTransRmtMemToLocMem(spec, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "out-of-range reduce data type accepted\n"; + return 3; + } + + spec.reduceDataType = 0; + spec.reduceOpCode = 0x10; + if (TileXRCcuEncodeTransLocMemToRmtMem(spec, &instr) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "out-of-range reduce op code accepted\n"; + return 4; + } + + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_microcode_builder_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = MICROCODE_HEADER.read_text(encoding="utf-8") + source = MICROCODE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_microcode.h", cmake) + self.assertIn("ccu/tilexr_ccu_microcode.cpp", cmake) + self.assertIn("struct TileXRCcuInstr", header) + self.assertIn("struct TileXRCcuSyncXnSpec", header) + self.assertIn("TileXRCcuEncodeLoadSqeArgsToX", header) + self.assertIn("TileXRCcuEncodeLoadImdToXn", header) + self.assertIn("TileXRCcuEncodeLoadImdToGsa", header) + self.assertIn("TileXRCcuEncodeSyncXn", header) + self.assertIn("TileXRCcuEncodeSyncCke", header) + self.assertIn("TileXRCcuEncodeSetCke", header) + self.assertIn("TileXRCcuEncodeClearCke", header) + self.assertIn("struct TileXRCcuMemTransferSpec", header) + self.assertIn("TileXRCcuEncodeTransRmtMemToLocMem", header) + self.assertIn("TileXRCcuEncodeTransLocMemToRmtMem", header) + self.assertIn("0x0001U", source) + self.assertIn("0x0002U", source) + self.assertIn("0x0003U", source) + self.assertIn("0x0802U", source) + self.assertIn("0x0804U", source) + self.assertIn("0x1008U", source) + self.assertIn("0x1009U", source) + self.assertIn("0x100bU", source) + self.assertIn("0x100dU", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_producer_plan.py b/tests/ccu/test_tilexr_ccu_producer_plan.py new file mode 100644 index 0000000..fc61ea2 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_producer_plan.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import subprocess +import tempfile +import textwrap +import unittest +import shutil +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +CCU_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.h" +CCU_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.cpp" +BARRIER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +RUNTIME_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_runtime.h" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalGsa = {1, 510, 20}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL, 0x100051152e00ULL, 0x0010017f86b7d29aULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuProducerPlanReport report; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_SUCCESS) { + std::cerr << "valid plan rejected: " << report.message << "\n"; + return 1; + } + if (report.syncResourceCount != 3 || report.taskCount != 2 || report.instructionCount != 170) { + std::cerr << "unexpected report counts\n"; + return 2; + } + + std::vector tasks; + if (TileXRCcuBuildTasks(plan, &tasks, &report) != TILEXR_SUCCESS) { + std::cerr << "task build failed: " << report.message << "\n"; + return 3; + } + if (tasks.size() != 2) { + std::cerr << "unexpected task size\n"; + return 4; + } + if (tasks[0].dieId != 1 || tasks[0].missionId != 6 || tasks[0].key != 0x059b0f03U || + tasks[0].instStartId != 489 || tasks[0].instCnt != 13 || tasks[0].argSize != 13 || + tasks[0].args[0] != 0x100051152e00ULL || tasks[0].args[2] != 0x0010017f86b7d29aULL) { + std::cerr << "unexpected sqe-load mission task\n"; + return 5; + } + if (tasks[1].dieId != 1 || tasks[1].missionId != 6 || tasks[1].key != 0x059b0f03U || + tasks[1].instStartId != 502 || tasks[1].instCnt != 143 || tasks[1].argSize != 13 || + tasks[1].args[0] != 0 || tasks[1].args[12] != 0) { + std::cerr << "unexpected sync mission task\n"; + return 6; + } + if (tasks[0].timeout != TILEXR_CCU_DEFAULT_TASK_TIMEOUT_SEC || + tasks[1].timeout != TILEXR_CCU_DEFAULT_TASK_TIMEOUT_SEC) { + std::cerr << "unexpected task timeout " << tasks[0].timeout + << " " << tasks[1].timeout << "\n"; + return 12; + } + TileXRCcuProgram program; + if (TileXRCcuBuildMicrocode(plan, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << report.message << "\n"; + return 7; + } + if (program.sqeLoad.size() != 13 || program.sync.size() != 11) { + std::cerr << "unexpected microcode sizes\n"; + return 8; + } + if (program.sqeLoad[0].words[0] != 0x0000000007a90001ULL || + program.sqeLoad[12].words[0] != 0x0000000c07b50001ULL) { + std::cerr << "unexpected sqe load microcode\n"; + return 9; + } + if (program.sync[0].words[0] != 0x0000000007a90001ULL || + program.sync[1].words[0] != 0x0000000107aa0001ULL || + program.sync[2].words[0] != 0x0000000007b60003ULL || + program.sync[3].words[0] != 0x0000000001fe0002ULL || + program.sync[4].words[0] != 0x0001016c00000802ULL || + program.sync[4].words[1] != 0) { + std::cerr << "unexpected hcomm-style task1 prelude\n"; + return 10; + } + if (program.sync[5].words[0] != 0x000007a90939100dULL || + program.sync[5].words[1] != 0x00000001016c0002ULL || + program.sync[5].words[2] != 0x0001000000000000ULL) { + std::cerr << "unexpected first sync microcode after prelude\n"; + return 10; + } + if (program.sync[8].words[0] != 0x0000000000010802ULL || + program.sync[8].words[1] != 0x000000000001016cULL) { + std::cerr << "unexpected sync wait microcode\n"; + return 11; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_plan_rejects_missing_producer_state(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_producer_plan.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + + TileXRCcuProducerPlanReport report; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing mission/key was accepted\n"; + return 1; + } + if (report.message.find("mission") == std::string::npos) { + std::cerr << "missing mission/key diagnostic was weak: " << report.message << "\n"; + return 2; + } + + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.syncResources[0].bindingCount = 0; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing channel binding was accepted\n"; + return 3; + } + if (report.message.find("binding") == std::string::npos) { + std::cerr << "missing binding diagnostic was weak: " << report.message << "\n"; + return 4; + } + + plan.syncResources[0].bindingCount = 1; + plan.syncResources[0].channelId = 0; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing channel id was accepted\n"; + return 5; + } + if (report.message.find("channel id") == std::string::npos) { + std::cerr << "missing channel id diagnostic was weak: " << report.message << "\n"; + return 6; + } + + plan.syncResources[0].channelId = 2; + plan.syncResources.push_back({1, 1962, 2362, 365, 2, 1}); + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "duplicate channel id was accepted\n"; + return 7; + } + if (report.message.find("duplicate channel id") == std::string::npos) { + std::cerr << "duplicate channel id diagnostic was weak: " << report.message << "\n"; + return 8; + } + plan.syncResources.pop_back(); + + plan.syncResources[0].localXn = 2040; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "out-of-range local xn was accepted\n"; + return 9; + } + if (report.message.find("local XN") == std::string::npos) { + std::cerr << "local xn diagnostic was weak: " << report.message << "\n"; + return 10; + } + + plan.syncResources[0].localXn = 1961; + plan.taskWindows[0].instStartId = 646; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "out-of-window task was accepted\n"; + return 11; + } + if (report.message.find("instruction") == std::string::npos) { + std::cerr << "instruction diagnostic was weak: " << report.message << "\n"; + return 12; + } + + plan.taskWindows[0].instStartId = 489; + plan.kernelLocalCke = {1, 0, 0}; + if (TileXRCcuValidateProducerPlan(plan, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing kernel-local cke repository was accepted\n"; + return 13; + } + if (report.message.find("CKE") == std::string::npos) { + std::cerr << "kernel-local cke diagnostic was weak: " << report.message << "\n"; + return 14; + } + + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_producer_plan_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = CCU_HEADER.read_text(encoding="utf-8") + source = CCU_SOURCE.read_text(encoding="utf-8") + runtime_header = RUNTIME_HEADER.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_producer_plan.h", cmake) + self.assertIn("ccu/tilexr_ccu_producer_plan.cpp", cmake) + self.assertIn('#include "ccu/tilexr_ccu_runtime.h"', header) + self.assertIn("TileXRCcuProducerPlan", header) + self.assertIn("TileXRCcuValidateProducerPlan", header) + self.assertIn("TileXRCcuBuildTasks", header) + self.assertIn("TileXRCcuBuildMicrocode", header) + self.assertIn("TileXRCcuProgram", header) + self.assertIn("localWaitCke", header) + self.assertIn("localWaitMask", header) + self.assertIn("remoteNotifyMask", header) + self.assertIn("tilexr_ccu_barrier_program.h", header) + self.assertIn("TileXRCcuBuildBarrierProgram", source) + self.assertIn("spec.localWaitCke", source) + self.assertIn("spec.remoteNotifyCke", source) + self.assertIn("TileXRCcuTask", runtime_header) + self.assertIn("kernelLocalCke", source) + self.assertIn("std::set channelIds", source) + self.assertIn("missing channel id for sync resource", source) + self.assertIn("duplicate channel id for sync resource", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_microcode_can_use_distinct_remote_notify_and_local_wait_cke(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_producer_plan.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 4}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 3; + resource.localWaitCke = 332; + resource.localWaitMask = 1; + resource.remoteNotifyMask = 1; + plan.syncResources.push_back(resource); + + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuProgram program; + TileXRCcuProducerPlanReport report; + if (TileXRCcuBuildMicrocode(plan, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << report.message << "\n"; + return 1; + } + if (program.sync.size() != 7) { + std::cerr << "unexpected sync size\n"; + return 2; + } + if (program.sync[5].words[1] != 0x00000001016c0002ULL) { + std::cerr << "post did not use remote notify CKE\n"; + return 3; + } + if (program.sync[6].words[0] != 0x0000000000010802ULL || + program.sync[6].words[1] != 0x000000000001014cULL) { + std::cerr << "wait/clear did not use local wait CKE\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_microcode_can_emit_sync_cke_barrier_mode_with_source_cke(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_producer_plan.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.barrierMode = TileXRCcuBarrierMode::SyncCke; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 0x220, 2}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 0x330; + resource.channelId = 2; + resource.bindingCount = 1; + resource.localWaitCke = 0x220; + resource.localWaitMask = 1; + resource.remoteNotifyMask = 1; + resource.sourceCke = 0x221; + resource.sourceCkeMask = 0xffff; + plan.syncResources.push_back(resource); + + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuProgram program; + TileXRCcuProducerPlanReport report; + if (TileXRCcuBuildMicrocode(plan, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "sync_cke microcode build failed: " << report.message << "\n"; + return 1; + } + if (program.sync.size() != 3) { + std::cerr << "unexpected sync_cke sync size\n"; + return 2; + } + if (program.sync[0].words[0] != 0xffff022100010802ULL || + program.sync[1].words[0] != 0x000102210330100bULL || + program.sync[1].words[1] != 0x0000000000000002ULL || + program.sync[2].words[0] != 0x0000000000010804ULL || + program.sync[2].words[1] != 0x0000000000010220ULL) { + std::cerr << "sync_cke barrier microcode mismatch\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_provider.py b/tests/ccu/test_tilexr_ccu_provider.py new file mode 100644 index 0000000..797bb62 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_provider.py @@ -0,0 +1,1618 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROVIDER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_provider.h" +PROVIDER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_provider.cpp" +PACKAGE_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_launch_package.h" +PACKAGE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_launch_package.cpp" +REPOSITORY_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_repository.cpp" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" +PRODUCER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.cpp" +BARRIER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 1}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 1}; + plan.barrierMode = TileXRCcuBarrierMode::LocalCkePostOnly; + + TileXRCcuSyncResource resource; + resource.dieId = 1; + resource.localXn = 1961; + resource.remoteXn = 2361; + resource.notifyCke = 364; + resource.channelId = 2; + resource.bindingCount = 1; + resource.localWaitCke = 332; + plan.syncResources.push_back(resource); + + plan.taskWindows.push_back({1, 489, 1, 13, {}}); + return plan; + } + + TileXRCcuEvidenceSource Source( + const TileXRCcuLaunchPackage& package, + const char* label, + bool endpointRouteVerified) + { + TileXRCcuEvidenceSource source; + source.kind = TileXRCcuEvidenceKind::PublicVerified; + source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + source.packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + source.deviceId = package.installScope.deviceId; + source.rank = package.installScope.rank; + source.provider = package.installScope.provider; + source.installAttemptReceiptId = 0xabc001ULL; + source.endpointRouteVerified = endpointRouteVerified; + source.source = std::string("unit-test-public-provider:") + label; + source.detail = std::string("audited ") + label + " evidence"; + return source; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed: " << packageReport.message << "\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 2; + } + + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.notifyCkeInstalled = true; + evidence.missionSource = Source(package, "mission", false); + evidence.repositorySource = Source(package, "repository", false); + evidence.localXnSource = Source(package, "local-xn", false); + evidence.notifyCkeSource = Source(package, "notify-cke", false); + + std::vector submitTasks; + TileXRCcuProviderReport report; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "local CKE post-only submit was rejected: " << report.message << "\n"; + return 3; + } + if (!report.submitReady || submitTasks.size() != package.tasks.size()) { + std::cerr << "local CKE submit readiness mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_gate_requires_complete_hardware_install_evidence(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + + TileXRCcuProviderReport report; + if (TileXRCcuValidateHardwareInstall(package, {}, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty install evidence was accepted\n"; + return 2; + } + if (report.message.find("mission") == std::string::npos) { + std::cerr << "empty install diagnostic was weak: " << report.message << "\n"; + return 3; + } + + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing channel binding was accepted\n"; + return 4; + } + if (report.message.find("channel") == std::string::npos) { + std::cerr << "channel diagnostic was weak: " << report.message << "\n"; + return 5; + } + + evidence.channelBindingsInstalled = true; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != TILEXR_SUCCESS) { + std::cerr << "complete install evidence was rejected: " << report.message << "\n"; + return 6; + } + if (report.taskCount != 2 || report.installedInstructionCount != 24 || + report.repositoryCount != 170 || report.submitReady || + report.message.find("validate-compatible") == std::string::npos) { + std::cerr << "unexpected provider report\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_gate_marks_package_submit_ready_only_after_install_evidence(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(plan, &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 8; + } + + std::vector submitTasks; + TileXRCcuProviderReport report; + if (TileXRCcuPrepareSubmitTasks(package, {}, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "uninstalled package became submit-ready\n"; + return 2; + } + if (!submitTasks.empty()) { + std::cerr << "failed prepare left submit tasks\n"; + return 3; + } + + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.missionSource.source = "unit-test-public-provider:mission"; + evidence.missionSource.detail = "audited mission/key evidence"; + evidence.missionSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.repositorySource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.repositorySource.source = "unit-test-public-provider:repository"; + evidence.repositorySource.detail = "audited repository evidence"; + evidence.repositorySource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.localXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.localXnSource.source = "unit-test-public-provider:local-xn"; + evidence.localXnSource.detail = "audited local XN evidence"; + evidence.localXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.remoteXnSource.source = "ValidateRemoteXnExchangeBindingProof"; + evidence.remoteXnSource.detail = + "remote XN peer exchange proof matches syncXn operands and verified endpoint route channel contexts"; + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.remoteXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.notifyCkeSource.source = "unit-test-public-provider:notify-cke"; + evidence.notifyCkeSource.detail = "audited notify CKE evidence"; + evidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.channelBindingSource.source = "unit-test-public-provider:channel-binding"; + evidence.channelBindingSource.detail = + "channel binding contexts installed via SET_PFE, SET_JETTY_CTX, SET_CHANNEL with verified endpoint routes"; + evidence.channelBindingSource.endpointRouteVerified = true; + evidence.channelBindingSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.missionSource.packageFingerprint = packageFingerprint; + evidence.repositorySource.packageFingerprint = packageFingerprint; + evidence.localXnSource.packageFingerprint = packageFingerprint; + evidence.remoteXnSource.packageFingerprint = packageFingerprint; + evidence.notifyCkeSource.packageFingerprint = packageFingerprint; + evidence.channelBindingSource.packageFingerprint = packageFingerprint; + evidence.missionSource.deviceId = 3; + evidence.repositorySource.deviceId = 3; + evidence.localXnSource.deviceId = 3; + evidence.remoteXnSource.deviceId = 3; + evidence.notifyCkeSource.deviceId = 3; + evidence.channelBindingSource.deviceId = 3; + evidence.missionSource.rank = 1; + evidence.repositorySource.rank = 1; + evidence.localXnSource.rank = 1; + evidence.remoteXnSource.rank = 1; + evidence.notifyCkeSource.rank = 1; + evidence.channelBindingSource.rank = 1; + evidence.missionSource.provider = "unit-test-public-install-provider"; + evidence.repositorySource.provider = "unit-test-public-install-provider"; + evidence.localXnSource.provider = "unit-test-public-install-provider"; + evidence.remoteXnSource.provider = "unit-test-public-install-provider"; + evidence.notifyCkeSource.provider = "unit-test-public-install-provider"; + evidence.channelBindingSource.provider = "unit-test-public-install-provider"; + evidence.missionSource.installAttemptReceiptId = 0xabc001ULL; + evidence.repositorySource.installAttemptReceiptId = 0xabc001ULL; + evidence.localXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.remoteXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.notifyCkeSource.installAttemptReceiptId = 0xabc001ULL; + evidence.channelBindingSource.installAttemptReceiptId = 0xabc001ULL; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "complete install evidence was rejected: " << report.message << "\n"; + return 4; + } + if (submitTasks.size() != 2 || submitTasks[0].instStartId != 489 || + submitTasks[0].instCnt != 13 || submitTasks[0].argSize != 13 || + submitTasks[1].instStartId != 502 || submitTasks[1].instCnt != 143 || + submitTasks[1].argSize != 13) { + std::cerr << "unexpected submit tasks\n"; + return 5; + } + if (!report.submitReady || report.message != "ok") { + std::cerr << "unexpected submit-ready report\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_rejects_legacy_bool_only_evidence_for_submit(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(plan, &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + + TileXRCcuProviderReport report; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != TILEXR_SUCCESS) { + std::cerr << "legacy bool install evidence should remain validate-compatible: " + << report.message << "\n"; + return 2; + } + if (report.submitReady || report.legacyEvidenceCount != 6) { + std::cerr << "legacy validate report incorrectly claimed submit-ready\n"; + return 3; + } + + std::vector submitTasks; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "legacy bool-only evidence became submit-ready\n"; + return 4; + } + if (!submitTasks.empty()) { + std::cerr << "failed legacy prepare left submit tasks\n"; + return 5; + } + if (report.submitReady || report.legacyEvidenceCount != 6) { + std::cerr << "unexpected failed prepare report\n"; + return 6; + } + if (report.message.find("submit requires public verified evidence") == std::string::npos) { + std::cerr << "legacy submit diagnostic was weak: " << report.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_requires_auditable_public_verified_evidence_sources(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuHardwareInstallEvidence VerifiedEvidence(const TileXRCcuLaunchPackage& package) + { + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.repositorySource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.localXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicVerified; + return evidence; + } + + void FillAuditSources(TileXRCcuHardwareInstallEvidence& evidence, const TileXRCcuLaunchPackage& package) + { + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + evidence.missionSource.source = "unit-test-public-provider:mission"; + evidence.missionSource.detail = "audited mission/key evidence"; + evidence.missionSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.missionSource.packageFingerprint = packageFingerprint; + evidence.repositorySource.source = "unit-test-public-provider:repository"; + evidence.repositorySource.detail = "audited repository evidence"; + evidence.repositorySource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.repositorySource.packageFingerprint = packageFingerprint; + evidence.localXnSource.source = "unit-test-public-provider:local-xn"; + evidence.localXnSource.detail = "audited local XN evidence"; + evidence.localXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.localXnSource.packageFingerprint = packageFingerprint; + evidence.remoteXnSource.source = "unit-test-public-provider:remote-xn"; + evidence.remoteXnSource.detail = "audited remote XN evidence"; + evidence.remoteXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.remoteXnSource.packageFingerprint = packageFingerprint; + evidence.notifyCkeSource.source = "unit-test-public-provider:notify-cke"; + evidence.notifyCkeSource.detail = "audited notify CKE evidence"; + evidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.notifyCkeSource.packageFingerprint = packageFingerprint; + evidence.channelBindingSource.source = "unit-test-public-provider:channel-binding"; + evidence.channelBindingSource.detail = "audited channel binding evidence"; + evidence.channelBindingSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingSource.endpointRouteVerified = true; + evidence.channelBindingSource.packageFingerprint = packageFingerprint; + evidence.missionSource.deviceId = package.installScope.deviceId; + evidence.repositorySource.deviceId = package.installScope.deviceId; + evidence.localXnSource.deviceId = package.installScope.deviceId; + evidence.remoteXnSource.deviceId = package.installScope.deviceId; + evidence.notifyCkeSource.deviceId = package.installScope.deviceId; + evidence.channelBindingSource.deviceId = package.installScope.deviceId; + evidence.missionSource.rank = package.installScope.rank; + evidence.repositorySource.rank = package.installScope.rank; + evidence.localXnSource.rank = package.installScope.rank; + evidence.remoteXnSource.rank = package.installScope.rank; + evidence.notifyCkeSource.rank = package.installScope.rank; + evidence.channelBindingSource.rank = package.installScope.rank; + evidence.missionSource.provider = package.installScope.provider; + evidence.repositorySource.provider = package.installScope.provider; + evidence.localXnSource.provider = package.installScope.provider; + evidence.remoteXnSource.provider = package.installScope.provider; + evidence.notifyCkeSource.provider = package.installScope.provider; + evidence.channelBindingSource.provider = package.installScope.provider; + evidence.missionSource.installAttemptReceiptId = 0xabc001ULL; + evidence.repositorySource.installAttemptReceiptId = 0xabc001ULL; + evidence.localXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.remoteXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.notifyCkeSource.installAttemptReceiptId = 0xabc001ULL; + evidence.channelBindingSource.installAttemptReceiptId = 0xabc001ULL; + } + + void MarkEndpointRoutesVerified(TileXRCcuHardwareInstallEvidence& evidence) + { + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.channelBindingSource.endpointRouteVerified = true; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 10; + } + + TileXRCcuHardwareInstallEvidence evidence = VerifiedEvidence(package); + TileXRCcuProviderReport report; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != TILEXR_SUCCESS) { + std::cerr << "validate compatibility rejected verified kind without audit strings: " + << report.message << "\n"; + return 2; + } + + std::vector submitTasks; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "public verified evidence without source/detail became submit-ready\n"; + return 3; + } + if (!submitTasks.empty()) { + std::cerr << "failed unaudited prepare left submit tasks\n"; + return 4; + } + if (report.message.find("public verified evidence source/detail required") == std::string::npos) { + std::cerr << "unaudited diagnostic was weak: " << report.message << "\n"; + return 5; + } + + FillAuditSources(evidence, package); + evidence.missionSource.surface = TileXRCcuEvidenceSurface::Unspecified; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "public verified evidence without provider surface became submit-ready\n"; + return 6; + } + if (report.message.find("public install provider evidence") == std::string::npos) { + std::cerr << "unclassified surface diagnostic was weak: " << report.message << "\n"; + return 7; + } + + evidence.missionSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.endpointRouteVerified = false; + evidence.channelBindingSource.endpointRouteVerified = false; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "generic endpoint evidence became submit-ready\n"; + return 8; + } + if (report.message.find("verified endpoint route") == std::string::npos) { + std::cerr << "endpoint provenance diagnostic was weak: " << report.message << "\n"; + return 9; + } + + MarkEndpointRoutesVerified(evidence); + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "audited public verified evidence was rejected: " << report.message << "\n"; + return 10; + } + if (submitTasks.size() != 2 || submitTasks[0].instStartId != 489 || + submitTasks[0].instCnt != 13 || submitTasks[1].instStartId != 502 || + submitTasks[1].instCnt != 143 || !report.submitReady || + report.publicVerifiedEvidenceCount != 6) { + std::cerr << "unexpected audited submit report\n"; + return 11; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_requires_install_evidence_to_match_launch_package_fingerprint(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan(uint32_t key, uint64_t firstArg) + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, key, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {firstArg}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuEvidenceSource Source(const char* label, const TileXRCcuLaunchPackage& package) + { + TileXRCcuEvidenceSource source; + source.kind = TileXRCcuEvidenceKind::PublicVerified; + source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + source.source = label; + const uint64_t fingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + source.detail = std::string("installed package fingerprint=") + std::to_string(fingerprint); + const std::string labelText(label); + if (labelText.find("remote-xn") != std::string::npos || + labelText.find("channel-binding") != std::string::npos) { + source.endpointRouteVerified = true; + } + source.packageFingerprint = fingerprint; + source.deviceId = package.installScope.deviceId; + source.rank = package.installScope.rank; + source.provider = package.installScope.provider; + source.installAttemptReceiptId = 0xabc001ULL; + return source; + } + + TileXRCcuHardwareInstallEvidence EvidenceFor(const TileXRCcuLaunchPackage& package) + { + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource = Source("unit-test-public-provider:mission", package); + evidence.repositorySource = Source("unit-test-public-provider:repository", package); + evidence.localXnSource = Source("unit-test-public-provider:local-xn", package); + evidence.remoteXnSource = Source("unit-test-public-provider:remote-xn", package); + evidence.notifyCkeSource = Source("unit-test-public-provider:notify-cke", package); + evidence.channelBindingSource = Source("unit-test-public-provider:channel-binding", package); + return evidence; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(0x059b0f03U, 0x100051152e00ULL), &package, &packageReport) != + TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 11; + } + const uint64_t fingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + if (fingerprint == 0) { + std::cerr << "package fingerprint was zero\n"; + return 2; + } + + TileXRCcuHardwareInstallEvidence evidence = EvidenceFor(package); + std::vector submitTasks; + TileXRCcuProviderReport report; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "matching evidence rejected: " << report.message << "\n"; + return 3; + } + + TileXRCcuLaunchPackage changedKeyPackage; + if (TileXRCcuBuildLaunchPackage( + MakePlan(0xe0ac084cU, 0x100051152e00ULL), &changedKeyPackage, &packageReport) != + TILEXR_SUCCESS) { + std::cerr << "changed-key package build failed\n"; + return 4; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &changedKeyPackage, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "changed-key scope bind failed\n"; + return 12; + } + if (TileXRCcuComputeLaunchPackageFingerprint(changedKeyPackage) == fingerprint) { + std::cerr << "changed key did not affect package fingerprint\n"; + return 5; + } + if (TileXRCcuPrepareSubmitTasks(changedKeyPackage, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "evidence for old key was accepted for changed package\n"; + return 6; + } + if (report.message.find("fingerprint") == std::string::npos) { + std::cerr << "fingerprint diagnostic was weak: " << report.message << "\n"; + return 7; + } + + TileXRCcuLaunchPackage changedArgsPackage; + if (TileXRCcuBuildLaunchPackage( + MakePlan(0x059b0f03U, 0x200051152e00ULL), &changedArgsPackage, &packageReport) != + TILEXR_SUCCESS) { + std::cerr << "changed-args package build failed\n"; + return 8; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &changedArgsPackage, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "changed-args scope bind failed\n"; + return 13; + } + if (TileXRCcuComputeLaunchPackageFingerprint(changedArgsPackage) == fingerprint) { + std::cerr << "changed task args did not affect package fingerprint\n"; + return 9; + } + if (TileXRCcuPrepareSubmitTasks(changedArgsPackage, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "evidence for old task args was accepted for changed package\n"; + return 10; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_requires_install_evidence_to_match_launch_scope(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuEvidenceSource Source( + const char* label, + const TileXRCcuLaunchPackage& package, + uint32_t deviceId, + uint32_t rank, + const char* provider) + { + TileXRCcuEvidenceSource source; + source.kind = TileXRCcuEvidenceKind::PublicVerified; + source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + source.source = label; + source.detail = "installed for explicit launch scope"; + const std::string labelText(label); + if (labelText.find("remote-xn") != std::string::npos || + labelText.find("channel-binding") != std::string::npos) { + source.endpointRouteVerified = true; + } + source.packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + source.deviceId = deviceId; + source.rank = rank; + source.provider = provider; + source.installAttemptReceiptId = 0xabc001ULL; + return source; + } + + TileXRCcuHardwareInstallEvidence EvidenceFor( + const TileXRCcuLaunchPackage& package, + uint32_t deviceId, + uint32_t rank, + const char* provider) + { + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource = Source("unit-test-public-provider:mission", package, deviceId, rank, provider); + evidence.repositorySource = + Source("unit-test-public-provider:repository", package, deviceId, rank, provider); + evidence.localXnSource = + Source("unit-test-public-provider:local-xn", package, deviceId, rank, provider); + evidence.remoteXnSource = + Source("unit-test-public-provider:remote-xn", package, deviceId, rank, provider); + evidence.notifyCkeSource = + Source("unit-test-public-provider:notify-cke", package, deviceId, rank, provider); + evidence.channelBindingSource = + Source("unit-test-public-provider:channel-binding", package, deviceId, rank, provider); + return evidence; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "failed to bind launch install scope\n"; + return 2; + } + + TileXRCcuProviderReport report; + std::vector submitTasks; + + TileXRCcuHardwareInstallEvidence wrongDevice = + EvidenceFor(package, 4, 1, "unit-test-public-install-provider"); + if (TileXRCcuPrepareSubmitTasks(package, wrongDevice, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "evidence from another device became submit-ready\n"; + return 3; + } + if (!submitTasks.empty() || report.submitReady || + report.message.find("device") == std::string::npos) { + std::cerr << "device-scope diagnostic was weak: " << report.message << "\n"; + return 4; + } + + TileXRCcuHardwareInstallEvidence wrongRank = + EvidenceFor(package, 3, 0, "unit-test-public-install-provider"); + if (TileXRCcuPrepareSubmitTasks(package, wrongRank, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "evidence from another rank became submit-ready\n"; + return 5; + } + if (!submitTasks.empty() || report.submitReady || + report.message.find("rank") == std::string::npos) { + std::cerr << "rank-scope diagnostic was weak: " << report.message << "\n"; + return 6; + } + + TileXRCcuHardwareInstallEvidence wrongProvider = + EvidenceFor(package, 3, 1, "other-public-install-provider"); + if (TileXRCcuPrepareSubmitTasks(package, wrongProvider, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "evidence from another provider became submit-ready\n"; + return 7; + } + if (!submitTasks.empty() || report.submitReady || + report.message.find("provider") == std::string::npos) { + std::cerr << "provider-scope diagnostic was weak: " << report.message << "\n"; + return 8; + } + + TileXRCcuHardwareInstallEvidence matching = + EvidenceFor(package, 3, 1, "unit-test-public-install-provider"); + if (TileXRCcuPrepareSubmitTasks(package, matching, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "matching scope evidence was rejected: " << report.message << "\n"; + return 9; + } + if (submitTasks.size() != package.tasks.size() || !report.submitReady) { + std::cerr << "unexpected matching scope submit state\n"; + return 10; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_requires_all_public_verified_evidence_from_same_install_receipt(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuEvidenceSource Source( + const char* label, + const TileXRCcuLaunchPackage& package, + uint64_t receipt) + { + TileXRCcuEvidenceSource source; + source.kind = TileXRCcuEvidenceKind::PublicVerified; + source.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + source.source = label; + source.detail = "installed for a single public install attempt"; + const std::string labelText(label); + if (labelText.find("remote-xn") != std::string::npos || + labelText.find("channel-binding") != std::string::npos) { + source.endpointRouteVerified = true; + } + source.packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + source.deviceId = package.installScope.deviceId; + source.rank = package.installScope.rank; + source.provider = package.installScope.provider; + source.installAttemptReceiptId = receipt; + return source; + } + + TileXRCcuHardwareInstallEvidence EvidenceFor( + const TileXRCcuLaunchPackage& package, + uint64_t receipt) + { + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource = Source("unit-test-public-provider:mission", package, receipt); + evidence.repositorySource = Source("unit-test-public-provider:repository", package, receipt); + evidence.localXnSource = Source("unit-test-public-provider:local-xn", package, receipt); + evidence.remoteXnSource = Source("unit-test-public-provider:remote-xn", package, receipt); + evidence.notifyCkeSource = Source("unit-test-public-provider:notify-cke", package, receipt); + evidence.channelBindingSource = Source("unit-test-public-provider:channel-binding", package, receipt); + return evidence; + } + + int ExpectReceiptFailure( + const TileXRCcuLaunchPackage& package, + const TileXRCcuHardwareInstallEvidence& evidence, + const char* expectedDiagnostic) + { + std::vector submitTasks; + TileXRCcuProviderReport report; + if (TileXRCcuPrepareSubmitTasks(package, evidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "receipt mismatch became submit-ready\n"; + return 1; + } + if (!submitTasks.empty() || report.submitReady || + report.message.find(expectedDiagnostic) == std::string::npos) { + std::cerr << "weak receipt diagnostic: " << report.message << "\n"; + return 2; + } + return 0; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "failed to bind launch install scope\n"; + return 2; + } + + TileXRCcuHardwareInstallEvidence missingReceipt = EvidenceFor(package, 0); + if (ExpectReceiptFailure(package, missingReceipt, "receipt") != 0) { + return 3; + } + + TileXRCcuHardwareInstallEvidence mixedReceipt = EvidenceFor(package, 0xabc001ULL); + mixedReceipt.notifyCkeSource.installAttemptReceiptId = 0xabc002ULL; + if (ExpectReceiptFailure(package, mixedReceipt, "receipt") != 0) { + return 4; + } + + TileXRCcuHardwareInstallEvidence matchingReceipt = EvidenceFor(package, 0xabc001ULL); + std::vector submitTasks; + TileXRCcuProviderReport report; + if (TileXRCcuPrepareSubmitTasks(package, matchingReceipt, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "matching receipt evidence was rejected: " << report.message << "\n"; + return 5; + } + if (submitTasks.size() != package.tasks.size() || !report.submitReady) { + std::cerr << "unexpected matching receipt submit state\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_prepare_rejects_public_verified_evidence_from_lower_layer_or_private_sources(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuHardwareInstallEvidence AuditedEvidence(const TileXRCcuLaunchPackage& package) + { + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.missionSource.source = "unit-test-public-provider:mission"; + evidence.missionSource.detail = "audited mission/key evidence"; + evidence.missionSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.missionSource.packageFingerprint = packageFingerprint; + evidence.repositorySource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.repositorySource.source = "unit-test-public-provider:repository"; + evidence.repositorySource.detail = "audited repository evidence"; + evidence.repositorySource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.repositorySource.packageFingerprint = packageFingerprint; + evidence.localXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.localXnSource.source = "unit-test-public-provider:local-xn"; + evidence.localXnSource.detail = "audited local XN evidence"; + evidence.localXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.localXnSource.packageFingerprint = packageFingerprint; + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.remoteXnSource.source = "unit-test-public-provider:remote-xn"; + evidence.remoteXnSource.detail = "audited remote XN evidence"; + evidence.remoteXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.remoteXnSource.packageFingerprint = packageFingerprint; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.notifyCkeSource.source = "unit-test-public-provider:notify-cke"; + evidence.notifyCkeSource.detail = "audited notify CKE evidence"; + evidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.notifyCkeSource.packageFingerprint = packageFingerprint; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.channelBindingSource.source = "unit-test-public-provider:channel-binding"; + evidence.channelBindingSource.detail = "audited channel binding evidence"; + evidence.channelBindingSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingSource.endpointRouteVerified = true; + evidence.channelBindingSource.packageFingerprint = packageFingerprint; + evidence.missionSource.deviceId = package.installScope.deviceId; + evidence.repositorySource.deviceId = package.installScope.deviceId; + evidence.localXnSource.deviceId = package.installScope.deviceId; + evidence.remoteXnSource.deviceId = package.installScope.deviceId; + evidence.notifyCkeSource.deviceId = package.installScope.deviceId; + evidence.channelBindingSource.deviceId = package.installScope.deviceId; + evidence.missionSource.rank = package.installScope.rank; + evidence.repositorySource.rank = package.installScope.rank; + evidence.localXnSource.rank = package.installScope.rank; + evidence.remoteXnSource.rank = package.installScope.rank; + evidence.notifyCkeSource.rank = package.installScope.rank; + evidence.channelBindingSource.rank = package.installScope.rank; + evidence.missionSource.provider = package.installScope.provider; + evidence.repositorySource.provider = package.installScope.provider; + evidence.localXnSource.provider = package.installScope.provider; + evidence.remoteXnSource.provider = package.installScope.provider; + evidence.notifyCkeSource.provider = package.installScope.provider; + evidence.channelBindingSource.provider = package.installScope.provider; + evidence.missionSource.installAttemptReceiptId = 0xabc001ULL; + evidence.repositorySource.installAttemptReceiptId = 0xabc001ULL; + evidence.localXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.remoteXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.notifyCkeSource.installAttemptReceiptId = 0xabc001ULL; + evidence.channelBindingSource.installAttemptReceiptId = 0xabc001ULL; + return evidence; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 8; + } + + TileXRCcuProviderReport report; + std::vector submitTasks; + + TileXRCcuHardwareInstallEvidence mappingEvidence = AuditedEvidence(package); + mappingEvidence.localXnSource.source = "unit-test-public-mapper:local-xn"; + mappingEvidence.localXnSource.detail = "maps existing XN resources but does not install them"; + mappingEvidence.localXnSource.surface = TileXRCcuEvidenceSurface::LowerLayerResourceHelper; + if (TileXRCcuValidateHardwareInstall(package, mappingEvidence, &report) != TILEXR_SUCCESS) { + std::cerr << "validate compatibility rejected mapping evidence: " << report.message << "\n"; + return 2; + } + if (TileXRCcuPrepareSubmitTasks(package, mappingEvidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "lower-layer mapping helper became submit-ready\n"; + return 3; + } + if (report.message.find("public install provider evidence") == std::string::npos || + report.message.find("lower-layer resource helper") == std::string::npos) { + std::cerr << "mapping diagnostic was weak: " << report.message << "\n"; + return 4; + } + + TileXRCcuHardwareInstallEvidence notifyEvidence = AuditedEvidence(package); + notifyEvidence.notifyCkeSource.source = "unit-test-public-notify-reader:notify-cke"; + notifyEvidence.notifyCkeSource.detail = "reads an existing notify address but does not install CKE"; + notifyEvidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::LowerLayerResourceHelper; + if (TileXRCcuPrepareSubmitTasks(package, notifyEvidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "lower-layer notify helper became submit-ready\n"; + return 5; + } + + TileXRCcuHardwareInstallEvidence privateEvidence = AuditedEvidence(package); + privateEvidence.repositorySource.source = "unit-test-private-observation:repository"; + privateEvidence.repositorySource.detail = "private repository install observation"; + privateEvidence.repositorySource.surface = TileXRCcuEvidenceSurface::PrivateProducerObservation; + if (TileXRCcuPrepareSubmitTasks(package, privateEvidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "private repository installer became submit-ready\n"; + return 6; + } + + TileXRCcuHardwareInstallEvidence audited = AuditedEvidence(package); + if (TileXRCcuPrepareSubmitTasks(package, audited, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "neutral audited evidence was rejected: " << report.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_gate_rejects_private_and_unvalidated_candidate_evidence(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + TileXRCcuHardwareInstallEvidence VerifiedEvidence(const TileXRCcuLaunchPackage& package) + { + const uint64_t packageFingerprint = TileXRCcuComputeLaunchPackageFingerprint(package); + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = true; + evidence.missionSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.missionSource.source = "unit-test-public-provider:mission"; + evidence.missionSource.detail = "audited mission/key evidence"; + evidence.missionSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.missionSource.packageFingerprint = packageFingerprint; + evidence.repositorySource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.repositorySource.source = "unit-test-public-provider:repository"; + evidence.repositorySource.detail = "audited repository evidence"; + evidence.repositorySource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.repositorySource.packageFingerprint = packageFingerprint; + evidence.localXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.localXnSource.source = "unit-test-public-provider:local-xn"; + evidence.localXnSource.detail = "audited local XN evidence"; + evidence.localXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.localXnSource.packageFingerprint = packageFingerprint; + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.remoteXnSource.source = "unit-test-public-provider:remote-xn"; + evidence.remoteXnSource.detail = "audited remote XN evidence"; + evidence.remoteXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.remoteXnSource.packageFingerprint = packageFingerprint; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.notifyCkeSource.source = "unit-test-public-provider:notify-cke"; + evidence.notifyCkeSource.detail = "audited notify CKE evidence"; + evidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.notifyCkeSource.packageFingerprint = packageFingerprint; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.channelBindingSource.source = "unit-test-public-provider:channel-binding"; + evidence.channelBindingSource.detail = "audited channel binding evidence"; + evidence.channelBindingSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingSource.endpointRouteVerified = true; + evidence.channelBindingSource.packageFingerprint = packageFingerprint; + evidence.missionSource.deviceId = package.installScope.deviceId; + evidence.repositorySource.deviceId = package.installScope.deviceId; + evidence.localXnSource.deviceId = package.installScope.deviceId; + evidence.remoteXnSource.deviceId = package.installScope.deviceId; + evidence.notifyCkeSource.deviceId = package.installScope.deviceId; + evidence.channelBindingSource.deviceId = package.installScope.deviceId; + evidence.missionSource.rank = package.installScope.rank; + evidence.repositorySource.rank = package.installScope.rank; + evidence.localXnSource.rank = package.installScope.rank; + evidence.remoteXnSource.rank = package.installScope.rank; + evidence.notifyCkeSource.rank = package.installScope.rank; + evidence.channelBindingSource.rank = package.installScope.rank; + evidence.missionSource.provider = package.installScope.provider; + evidence.repositorySource.provider = package.installScope.provider; + evidence.localXnSource.provider = package.installScope.provider; + evidence.remoteXnSource.provider = package.installScope.provider; + evidence.notifyCkeSource.provider = package.installScope.provider; + evidence.channelBindingSource.provider = package.installScope.provider; + evidence.missionSource.installAttemptReceiptId = 0xabc001ULL; + evidence.repositorySource.installAttemptReceiptId = 0xabc001ULL; + evidence.localXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.remoteXnSource.installAttemptReceiptId = 0xabc001ULL; + evidence.notifyCkeSource.installAttemptReceiptId = 0xabc001ULL; + evidence.channelBindingSource.installAttemptReceiptId = 0xabc001ULL; + return evidence; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + if (TileXRCcuBindLaunchPackageInstallScope( + &package, 3, 1, "unit-test-public-install-provider") != TILEXR_SUCCESS) { + std::cerr << "scope bind failed\n"; + return 9; + } + + TileXRCcuProviderReport report; + TileXRCcuHardwareInstallEvidence privateEvidence = VerifiedEvidence(package); + privateEvidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PrivateObserved; + privateEvidence.remoteXnSource.source = "hcomm::CcuKernel::CreateVariable"; + if (TileXRCcuValidateHardwareInstall(package, privateEvidence, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "private remote XN evidence was accepted\n"; + return 2; + } + if (report.message.find("remote XN") == std::string::npos || + report.message.find("private") == std::string::npos) { + std::cerr << "private evidence diagnostic was weak: " << report.message << "\n"; + return 3; + } + + TileXRCcuHardwareInstallEvidence candidateEvidence = VerifiedEvidence(package); + candidateEvidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicCandidate; + candidateEvidence.channelBindingSource.source = "rtCcuBindChannel"; + if (TileXRCcuPrepareSubmitTasks(package, candidateEvidence, nullptr, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null submit vector was not rejected first\n"; + return 4; + } + + std::vector submitTasks; + if (TileXRCcuPrepareSubmitTasks(package, candidateEvidence, &submitTasks, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "public candidate channel binding was accepted\n"; + return 5; + } + if (!submitTasks.empty()) { + std::cerr << "candidate prepare left submit tasks\n"; + return 6; + } + if (report.message.find("channel") == std::string::npos || + report.message.find("candidate") == std::string::npos) { + std::cerr << "candidate diagnostic was weak: " << report.message << "\n"; + return 7; + } + + TileXRCcuHardwareInstallEvidence verified = VerifiedEvidence(package); + if (TileXRCcuPrepareSubmitTasks(package, verified, &submitTasks, &report) != TILEXR_SUCCESS) { + std::cerr << "public verified evidence was rejected: " << report.message << "\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_report_counts_evidence_kinds_for_auditing(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_provider.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuLaunchPackage package; + TileXRCcuLaunchPackageReport packageReport; + if (TileXRCcuBuildLaunchPackage(MakePlan(), &package, &packageReport) != TILEXR_SUCCESS) { + std::cerr << "package build failed\n"; + return 1; + } + + TileXRCcuHardwareInstallEvidence evidence; + evidence.missionInstalled = true; + evidence.repositoryInstalled = true; + evidence.localXnInstalled = true; + evidence.remoteXnBound = true; + evidence.notifyCkeInstalled = true; + evidence.channelBindingsInstalled = false; + evidence.missionSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.repositorySource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.localXnSource.kind = TileXRCcuEvidenceKind::LegacyBoolean; + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PrivateObserved; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicCandidate; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::Missing; + + TileXRCcuProviderReport report; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "mixed bad evidence was accepted\n"; + return 2; + } + if (report.evidenceBitCount != 6 || + report.publicVerifiedEvidenceCount != 2 || + report.legacyEvidenceCount != 1 || + report.privateObservedEvidenceCount != 1 || + report.publicCandidateEvidenceCount != 1 || + report.missingEvidenceCount != 1) { + std::cerr << "unexpected evidence counters: bits=" << report.evidenceBitCount + << " public=" << report.publicVerifiedEvidenceCount + << " legacy=" << report.legacyEvidenceCount + << " private=" << report.privateObservedEvidenceCount + << " candidate=" << report.publicCandidateEvidenceCount + << " missing=" << report.missingEvidenceCount << "\n"; + return 3; + } + if (report.message.find("remote XN") == std::string::npos || + report.message.find("private") == std::string::npos) { + std::cerr << "first rejection diagnostic was weak: " << report.message << "\n"; + return 4; + } + + evidence.remoteXnSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.remoteXnSource.source = "unit-test-public-provider:remote-xn"; + evidence.remoteXnSource.detail = "audited remote XN evidence"; + evidence.remoteXnSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.remoteXnSource.endpointRouteVerified = true; + evidence.notifyCkeSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.notifyCkeSource.source = "unit-test-public-provider:notify-cke"; + evidence.notifyCkeSource.detail = "audited notify CKE evidence"; + evidence.notifyCkeSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingsInstalled = true; + evidence.channelBindingSource.kind = TileXRCcuEvidenceKind::PublicVerified; + evidence.channelBindingSource.source = "unit-test-public-provider:channel-binding"; + evidence.channelBindingSource.detail = "audited channel binding evidence"; + evidence.channelBindingSource.surface = TileXRCcuEvidenceSurface::PublicInstallProvider; + evidence.channelBindingSource.endpointRouteVerified = true; + if (TileXRCcuValidateHardwareInstall(package, evidence, &report) != TILEXR_SUCCESS) { + std::cerr << "fixed evidence was rejected: " << report.message << "\n"; + return 5; + } + if (report.evidenceBitCount != 6 || + report.publicVerifiedEvidenceCount != 5 || + report.legacyEvidenceCount != 1 || + report.privateObservedEvidenceCount != 0 || + report.publicCandidateEvidenceCount != 0 || + report.missingEvidenceCount != 0 || + report.submitReady) { + std::cerr << "unexpected ready evidence counters\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_layer_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = PROVIDER_HEADER.read_text(encoding="utf-8") + source = PROVIDER_SOURCE.read_text(encoding="utf-8") + package_header = PACKAGE_HEADER.read_text(encoding="utf-8") + package_source = PACKAGE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_provider.h", cmake) + self.assertIn("ccu/tilexr_ccu_provider.cpp", cmake) + self.assertIn("struct TileXRCcuHardwareInstallEvidence", header) + self.assertIn("enum class TileXRCcuEvidenceKind", header) + self.assertIn("enum class TileXRCcuEvidenceSurface", header) + self.assertIn("struct TileXRCcuEvidenceSource", header) + self.assertIn("TileXRCcuValidateHardwareInstall", header) + self.assertIn("TileXRCcuPrepareSubmitTasks", header) + self.assertIn("tilexr_ccu_launch_package.h", header) + self.assertIn("submitReady", header) + self.assertIn("evidenceBitCount", header) + self.assertIn("publicVerifiedEvidenceCount", header) + self.assertIn("publicCandidateEvidenceCount", header) + self.assertIn("privateObservedEvidenceCount", header) + self.assertIn("missingEvidenceCount", header) + self.assertIn("uint64_t packageFingerprint", header) + self.assertIn("uint32_t deviceId", header) + self.assertIn("uint32_t rank", header) + self.assertIn("std::string provider", header) + self.assertIn("uint64_t installAttemptReceiptId", header) + self.assertIn("TileXRCcuComputeLaunchPackageFingerprint", package_header) + self.assertIn("TileXRCcuComputeLaunchPackageFingerprint", package_source) + self.assertIn("package fingerprint mismatch", source) + self.assertIn("TileXRCcuLaunchInstallScope", package_header) + self.assertIn("installScope", package_header) + self.assertIn("TileXRCcuBindLaunchPackageInstallScope", package_header) + self.assertIn("device scope mismatch", source) + self.assertIn("rank scope mismatch", source) + self.assertIn("provider scope mismatch", source) + self.assertIn("install attempt receipt", source) + self.assertIn("endpointRouteVerified", header) + self.assertIn("submit requires verified endpoint route evidence", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_prepare_path_requires_public_verified_evidence_source(self): + source = PROVIDER_SOURCE.read_text(encoding="utf-8") + prepare_start = source.index("int TileXRCcuPrepareSubmitTasks(") + prepare_body = source[prepare_start:] + + validate_pos = prepare_body.index("TileXRCcuValidateHardwareInstall(package, evidence, report)") + submit_gate_pos = prepare_body.index("ValidateSubmitEvidence(package, evidence, report)") + copy_pos = prepare_body.index("*submitTasks = package.tasks") + + self.assertIn("submit requires public verified evidence", source) + self.assertLess(validate_pos, submit_gate_pos) + self.assertLess(submit_gate_pos, copy_pos) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_public_api_compile_probe.py b/tests/ccu/test_tilexr_ccu_public_api_compile_probe.py new file mode 100644 index 0000000..da73b90 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_public_api_compile_probe.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROBE_C = REPO_ROOT / "tests" / "ccu" / "ccu_public_direct_api_compile_probe.c" +INCLUDE_DIR = REPO_ROOT / "src" / "include" + + +class TileXRCcuPublicApiCompileProbeTest(unittest.TestCase): + def test_probe_source_uses_only_public_api_header(self): + source = PROBE_C.read_text(encoding="utf-8") + + self.assertIn('#include "tilexr_api.h"', source) + for needle in [ + '#include "tilexr_comm.h"', + '#include "ccu/', + "#include ", source) + self.assertNotIn("TileXRCcuDirectInstallAttempt", source) + self.assertNotIn("TileXRCcuSubmitPreparedTasks", source) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_ra_custom_channel_loader.py b/tests/ccu/test_tilexr_ccu_ra_custom_channel_loader.py new file mode 100644 index 0000000..9190327 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_ra_custom_channel_loader.py @@ -0,0 +1,2110 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +LOADER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.h" +LOADER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_loader.cpp" +TYPES_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_hccp_types.h" +ABI_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_abi_constants.h" +DIRECT_RUNTIME_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_direct_runtime.h" +DIRECT_RUNTIME_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_direct_runtime.cpp" +DRIVER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.h" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" + + +class TileXRCcuRaCustomChannelLoaderTest(unittest.TestCase): + def compile_and_run(self, code: str, extra_sources=None, extra_link_flags=None, extra_env=None): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + extra_sources = extra_sources or [] + extra_link_flags = extra_link_flags or [] + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "hccp_loader_test.cpp" + test_bin = temp_path / "hccp_loader_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(REPO_ROOT / "src" / "include"), + "-I", + str(REPO_ROOT / "src" / "comm"), + str(test_cpp), + *[str(source) for source in extra_sources], + "-o", + str(test_bin), + *extra_link_flags, + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + env = None if extra_env is None else {**extra_env} + return subprocess.run( + [str(test_bin)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env, + ) + + def test_ccu_hccp_loader_exposes_ra_custom_channel_for_tilexr_ccu(self): + header = LOADER_HEADER.read_text(encoding="utf-8") + source = LOADER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TileXRCcuRaCustomChannelFunc", TYPES_HEADER.read_text(encoding="utf-8")) + self.assertIn("RaCustomChannel", header) + self.assertIn("RaCustomChannel = nullptr", header) + self.assertIn('LoadSymbol(raHandle_, RaCustomChannel, "RaCustomChannel", "ra_custom_channel")', source) + self.assertIn("RaCustomChannel = nullptr", source) + + def test_ccu_hccp_loader_exposes_runtime_phy_id_mapping_for_ccu_runtime(self): + header = LOADER_HEADER.read_text(encoding="utf-8") + source = LOADER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TileXRCcuRtGetDevicePhyIdByIndexFunc", TYPES_HEADER.read_text(encoding="utf-8")) + self.assertIn("RtGetDevicePhyIdByIndex", header) + self.assertIn("RtGetDevicePhyIdByIndex = nullptr", header) + self.assertIn('dlopen("libruntime.so", RTLD_NOW)', source) + self.assertIn( + 'LoadOptionalSymbol(runtimeHandle_, RtGetDevicePhyIdByIndex, "rtGetDevicePhyIdByIndex", nullptr)', + source, + ) + self.assertIn("ResolveDevicePhyId", header) + self.assertIn("ResolveDevicePhyId", source) + + def test_ccu_hccp_loader_exposes_public_ra_ctx_resource_window_symbols(self): + header = LOADER_HEADER.read_text(encoding="utf-8") + source = LOADER_SOURCE.read_text(encoding="utf-8") + types = TYPES_HEADER.read_text(encoding="utf-8") + + for needle in [ + "TileXRCcuRaGetDevEidInfoNumFunc", + "TileXRCcuRaGetDevEidInfoListFunc", + "TileXRCcuRaCtxInitFunc", + "TileXRCcuRaCtxDeinitFunc", + "TileXRCcuRaCtxTokenIdAllocFunc", + "TileXRCcuRaCtxTokenIdFreeFunc", + "TileXRCcuRaCtxLmemRegisterFunc", + "TileXRCcuRaCtxLmemUnregisterFunc", + "TileXRCcuRaGetSecRandomFunc", + "TileXRCcuHccpMrRegInfo", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, types) + + for needle in [ + "RaGetDevEidInfoNum", + "RaGetDevEidInfoList", + "RaCtxInit", + "RaCtxDeinit", + "RaCtxTokenIdAlloc", + "RaCtxTokenIdFree", + "RaCtxLmemRegister", + "RaCtxLmemUnregister", + "RaGetSecRandom", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, header) + self.assertIn(needle, source) + + def test_ccu_hccp_loader_exposes_public_ra_ctx_endpoint_route_symbols(self): + header = LOADER_HEADER.read_text(encoding="utf-8") + source = LOADER_SOURCE.read_text(encoding="utf-8") + types = TYPES_HEADER.read_text(encoding="utf-8") + + for needle in [ + "TileXRCcuRaCtxCqCreateFunc", + "TileXRCcuRaCtxCqDestroyFunc", + "TileXRCcuRaCtxQpCreateFunc", + "TileXRCcuRaCtxQpDestroyFunc", + "TileXRCcuRaCtxQpImportFunc", + "TileXRCcuRaCtxQpUnimportFunc", + "TileXRCcuRaCtxQpBindFunc", + "TileXRCcuRaCtxQpUnbindFunc", + "TileXRCcuRaGetTpInfoListAsyncFunc", + "TileXRCcuRaGetAsyncReqResultFunc", + "TileXRCcuHccpQpCreateAttr", + "TileXRCcuHccpQpImportInfo", + "TileXRCcuHccpGetTpCfg", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, types) + + for needle in [ + "RaCtxCqCreate", + "RaCtxCqDestroy", + "RaCtxQpCreate", + "RaCtxQpDestroy", + "RaCtxQpImport", + "RaCtxQpUnimport", + "RaCtxQpBind", + "RaCtxQpUnbind", + "RaGetTpInfoListAsync", + "RaGetAsyncReqResult", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, header) + self.assertIn(needle, source) + + def test_ccu_hccp_loader_fails_closed_when_runtime_phy_id_mapping_fails(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + int FakeRtGetDevicePhyIdByIndex(uint32_t logicDevId, uint32_t* phyId) + { + if (logicDevId != 7 || phyId == nullptr) { + return -99; + } + *phyId = 0xbeefU; + return -1234; + } + + int main() + { + TileXRCcuHccpLoader loader; + loader.loaded_ = true; + loader.RtGetDevicePhyIdByIndex = FakeRtGetDevicePhyIdByIndex; + + uint32_t phyId = 0; + TileXRCcuHccpLoaderReport report; + const int ret = loader.ResolveDevicePhyId(7, &phyId, &report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "phy id mapping failure silently succeeded\n"; + return 1; + } + if (phyId == 7 || phyId == 0xbeefU) { + std::cerr << "failed mapping leaked fallback phy id: " << phyId << "\n"; + return 2; + } + if (report.message.find("rtGetDevicePhyIdByIndex failed") == std::string::npos) { + std::cerr << "missing phy id failure message: " << report.message << "\n"; + return 3; + } + if (report.logicDevId != 7 || report.runtimePhyIdRet != -1234) { + std::cerr << "phy id report mismatch\n"; + return 4; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_initializes_and_deinitializes_ra_hdc_session(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + TileXRCcuRaInitConfig g_lastInitConfig {}; + TileXRCcuRaInitConfig g_lastDeinitConfig {}; + int g_initCalls = 0; + int g_deinitCalls = 0; + int g_openCalls = 0; + int g_closeCalls = 0; + + int FakeRtOpenNetService(const TileXRCcuRtNetServiceOpenArgs*) + { + ++g_openCalls; + return 0; + } + + int FakeRtCloseNetService() + { + ++g_closeCalls; + return 0; + } + + int FakeRaInit(TileXRCcuRaInitConfig* config) + { + if (config == nullptr) { + return -9; + } + g_lastInitConfig = *config; + ++g_initCalls; + return 0; + } + + int FakeRaDeinit(TileXRCcuRaInitConfig* config) + { + if (config == nullptr) { + return -8; + } + g_lastDeinitConfig = *config; + ++g_deinitCalls; + return 0; + } + + int main() + { + TileXRCcuHccpLoader first; + first.loaded_ = true; + first.RtOpenNetService = FakeRtOpenNetService; + first.RtCloseNetService = FakeRtCloseNetService; + first.RaInit = FakeRaInit; + first.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoader second; + second.loaded_ = true; + second.RtOpenNetService = FakeRtOpenNetService; + second.RtCloseNetService = FakeRtCloseNetService; + second.RaInit = FakeRaInit; + second.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoaderReport report; + if (first.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report) != TILEXR_SUCCESS) { + std::cerr << "first init failed: " << report.message << "\n"; + return 1; + } + if (second.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report) != TILEXR_SUCCESS) { + std::cerr << "second init failed: " << report.message << "\n"; + return 2; + } + if (g_initCalls != 1 || !report.raInitialized || report.raInitRefCount != 2) { + std::cerr << "RA init refcount mismatch\n"; + return 3; + } + if (g_openCalls != 1 || report.netServiceRefCount != 2) { + std::cerr << "net service refcount mismatch\n"; + return 8; + } + if (g_lastInitConfig.phyId != 3 || + g_lastInitConfig.nicPosition != TILEXR_CCU_NETWORK_OFFLINE || + g_lastInitConfig.hdcType != TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2 || + !g_lastInitConfig.enableHdcAsync) { + std::cerr << "RA init config mismatch\n"; + return 4; + } + + second.Unload(); + if (g_deinitCalls != 0) { + std::cerr << "RA deinit happened before last reference\n"; + return 5; + } + if (g_closeCalls != 0) { + std::cerr << "runtime net service closed before last reference\n"; + return 9; + } + first.Unload(); + if (g_deinitCalls != 1) { + std::cerr << "RA deinit count mismatch: " << g_deinitCalls << "\n"; + return 6; + } + if (g_closeCalls != 1) { + std::cerr << "runtime net service close count mismatch: " << g_closeCalls << "\n"; + return 10; + } + if (g_lastDeinitConfig.phyId != 3 || + g_lastDeinitConfig.nicPosition != TILEXR_CCU_NETWORK_OFFLINE || + g_lastDeinitConfig.hdcType != TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2) { + std::cerr << "RA deinit config mismatch\n"; + return 7; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_opens_runtime_net_service_before_ra_init(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + #include + #include + + using namespace TileXR; + + std::string g_openArg; + int g_openCalls = 0; + int g_closeCalls = 0; + int g_initCalls = 0; + int g_order = 0; + int g_openOrder = 0; + int g_initOrder = 0; + + int FakeRtOpenNetService(const TileXRCcuRtNetServiceOpenArgs* args) + { + ++g_openCalls; + g_openOrder = ++g_order; + if (args == nullptr || args->extParamList == nullptr || args->extParamCnt != 1 || + args->extParamList[0].paramInfo == nullptr) { + return -11; + } + g_openArg.assign(args->extParamList[0].paramInfo, args->extParamList[0].paramLen); + return 0; + } + + int FakeRtCloseNetService() + { + ++g_closeCalls; + return 0; + } + + int FakeRaInit(TileXRCcuRaInitConfig*) + { + ++g_initCalls; + g_initOrder = ++g_order; + return 0; + } + + int FakeRaDeinit(TileXRCcuRaInitConfig*) + { + return 0; + } + + int main() + { + TileXRCcuHccpLoader loader; + loader.loaded_ = true; + loader.RtOpenNetService = FakeRtOpenNetService; + loader.RtCloseNetService = FakeRtCloseNetService; + loader.RaInit = FakeRaInit; + loader.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoaderReport report; + if (loader.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + if (g_openCalls != 1 || g_initCalls != 1 || g_openArg != "--hdcType=18") { + std::cerr << "open/init mismatch open=" << g_openCalls + << " init=" << g_initCalls + << " arg=" << g_openArg << "\n"; + return 2; + } + if (g_openOrder == 0 || g_initOrder == 0 || g_openOrder > g_initOrder) { + std::cerr << "rtOpenNetService did not precede RaInit\n"; + return 3; + } + loader.Unload(); + if (g_closeCalls != 1) { + std::cerr << "close count mismatch: " << g_closeCalls << "\n"; + return 4; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_refcounts_runtime_net_service_by_hdc_type(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + int g_openCalls = 0; + int g_closeCalls = 0; + int g_initCalls = 0; + int g_deinitCalls = 0; + + int FakeRtOpenNetService(const TileXRCcuRtNetServiceOpenArgs*) + { + ++g_openCalls; + return 0; + } + + int FakeRtCloseNetService() + { + ++g_closeCalls; + return 0; + } + + int FakeRaInit(TileXRCcuRaInitConfig*) + { + ++g_initCalls; + return 0; + } + + int FakeRaDeinit(TileXRCcuRaInitConfig*) + { + ++g_deinitCalls; + return 0; + } + + int main() + { + TileXRCcuHccpLoader first; + first.loaded_ = true; + first.RtOpenNetService = FakeRtOpenNetService; + first.RtCloseNetService = FakeRtCloseNetService; + first.RaInit = FakeRaInit; + first.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoader second; + second.loaded_ = true; + second.RtOpenNetService = FakeRtOpenNetService; + second.RtCloseNetService = FakeRtCloseNetService; + second.RaInit = FakeRaInit; + second.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoaderReport report; + if (first.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report) != TILEXR_SUCCESS || + second.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + if (g_openCalls != 1 || g_initCalls != 1 || report.raInitRefCount != 2) { + std::cerr << "open/init ref mismatch open=" << g_openCalls + << " init=" << g_initCalls + << " refs=" << report.raInitRefCount << "\n"; + return 2; + } + second.Unload(); + if (g_closeCalls != 0 || g_deinitCalls != 0) { + std::cerr << "closed/deinit before final reference\n"; + return 3; + } + first.Unload(); + if (g_closeCalls != 1 || g_deinitCalls != 1) { + std::cerr << "final close/deinit mismatch close=" << g_closeCalls + << " deinit=" << g_deinitCalls << "\n"; + return 4; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_fails_before_ra_init_when_net_service_open_fails(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + int g_openCalls = 0; + int g_initCalls = 0; + + int FakeRtOpenNetService(const TileXRCcuRtNetServiceOpenArgs*) + { + ++g_openCalls; + return 128000; + } + + int FakeRtCloseNetService() + { + return 0; + } + + int FakeRaInit(TileXRCcuRaInitConfig*) + { + ++g_initCalls; + return 0; + } + + int FakeRaDeinit(TileXRCcuRaInitConfig*) + { + return 0; + } + + int main() + { + TileXRCcuHccpLoader loader; + loader.loaded_ = true; + loader.RtOpenNetService = FakeRtOpenNetService; + loader.RtCloseNetService = FakeRtCloseNetService; + loader.RaInit = FakeRaInit; + loader.RaDeinit = FakeRaDeinit; + + TileXRCcuHccpLoaderReport report; + const int ret = loader.InitRaHdc(3, TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2, true, &report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "open failure unexpectedly succeeded\n"; + return 1; + } + if (g_openCalls != 1 || g_initCalls != 0) { + std::cerr << "open failure call mismatch open=" << g_openCalls + << " init=" << g_initCalls << "\n"; + return 2; + } + if (report.message.find("rtOpenNetService failed ret=128000") == std::string::npos || + report.message.find("--hdcType=18") == std::string::npos) { + std::cerr << "missing open failure diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_initializes_and_releases_ccu_tlv_session(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + TileXRCcuTlvInitInfo g_lastInitInfo {}; + uint32_t g_lastRequestModule = 0; + uint32_t g_lastRequestType = 0; + uint32_t g_lastReleaseType = 0; + int g_initCalls = 0; + int g_requestCalls = 0; + int g_deinitCalls = 0; + int g_order = 0; + int g_initOrder = 0; + int g_requestOrder = 0; + int g_deinitOrder = 0; + + int FakeRaTlvInit(TileXRCcuTlvInitInfo* initInfo, uint32_t* bufferSize, void** tlvHandle) + { + ++g_initCalls; + g_initOrder = ++g_order; + if (initInfo == nullptr || bufferSize == nullptr || tlvHandle == nullptr) { + return -1; + } + g_lastInitInfo = *initInfo; + *bufferSize = 4096; + *tlvHandle = reinterpret_cast(0x12345678ULL); + return 0; + } + + int FakeRaTlvRequest(void* tlvHandle, uint32_t moduleType, TileXRCcuTlvMsg* sendMsg, TileXRCcuTlvMsg* recvMsg) + { + ++g_requestCalls; + g_requestOrder = ++g_order; + if (tlvHandle != reinterpret_cast(0x12345678ULL) || sendMsg == nullptr || recvMsg == nullptr) { + return -2; + } + g_lastRequestModule = moduleType; + if (sendMsg->type == TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT) { + g_lastReleaseType = sendMsg->type; + } else { + g_lastRequestType = sendMsg->type; + } + return 0; + } + + int FakeRaTlvDeinit(void* tlvHandle) + { + ++g_deinitCalls; + g_deinitOrder = ++g_order; + return tlvHandle == reinterpret_cast(0x12345678ULL) ? 0 : -3; + } + + int main() + { + TileXRCcuHccpLoader loader; + loader.loaded_ = true; + loader.RaTlvInit = FakeRaTlvInit; + loader.RaTlvRequest = FakeRaTlvRequest; + loader.RaTlvDeinit = FakeRaTlvDeinit; + + TileXRCcuHccpLoaderReport report; + if (loader.InitCcuTlv(3, &report) != TILEXR_SUCCESS) { + std::cerr << "tlv init failed: " << report.message << "\n"; + return 1; + } + if (!report.ccuTlvInitialized || report.ccuTlvRefCount != 1 || + report.ccuTlvBufferSize != 4096) { + std::cerr << "tlv report mismatch\n"; + return 2; + } + if (g_initCalls != 1 || g_requestCalls != 1 || g_deinitCalls != 0) { + std::cerr << "tlv init call count mismatch\n"; + return 3; + } + if (g_lastInitInfo.version != TILEXR_CCU_TLV_VERSION || + g_lastInitInfo.phyId != 3 || + g_lastInitInfo.nicPosition != TILEXR_CCU_NETWORK_OFFLINE || + g_lastRequestModule != TILEXR_CCU_TLV_MODULE_TYPE_CCU || + g_lastRequestType != TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT) { + std::cerr << "tlv init/request envelope mismatch\n"; + return 4; + } + if (g_initOrder == 0 || g_requestOrder == 0 || g_initOrder > g_requestOrder) { + std::cerr << "tlv request did not follow init\n"; + return 5; + } + + loader.Unload(); + if (g_requestCalls != 2 || g_lastReleaseType != TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT || + g_deinitCalls != 1) { + std::cerr << "tlv release mismatch requestCalls=" << g_requestCalls + << " releaseType=" << g_lastReleaseType + << " deinitCalls=" << g_deinitCalls << "\n"; + return 6; + } + if (g_deinitOrder == 0 || g_requestOrder > g_deinitOrder) { + std::cerr << "tlv deinit happened before release request\n"; + return 7; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_refcounts_ccu_tlv_by_device_phy_id(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + int g_initCalls = 0; + int g_requestCalls = 0; + int g_deinitCalls = 0; + + int FakeRaTlvInit(TileXRCcuTlvInitInfo*, uint32_t* bufferSize, void** tlvHandle) + { + ++g_initCalls; + *bufferSize = 1024; + *tlvHandle = reinterpret_cast(0x1000ULL); + return 0; + } + + int FakeRaTlvRequest(void*, uint32_t, TileXRCcuTlvMsg*, TileXRCcuTlvMsg*) + { + ++g_requestCalls; + return 0; + } + + int FakeRaTlvDeinit(void*) + { + ++g_deinitCalls; + return 0; + } + + int main() + { + TileXRCcuHccpLoader first; + first.loaded_ = true; + first.RaTlvInit = FakeRaTlvInit; + first.RaTlvRequest = FakeRaTlvRequest; + first.RaTlvDeinit = FakeRaTlvDeinit; + + TileXRCcuHccpLoader second; + second.loaded_ = true; + second.RaTlvInit = FakeRaTlvInit; + second.RaTlvRequest = FakeRaTlvRequest; + second.RaTlvDeinit = FakeRaTlvDeinit; + + TileXRCcuHccpLoaderReport report; + if (first.InitCcuTlv(3, &report) != TILEXR_SUCCESS || + second.InitCcuTlv(3, &report) != TILEXR_SUCCESS) { + std::cerr << "tlv init failed: " << report.message << "\n"; + return 1; + } + if (g_initCalls != 1 || g_requestCalls != 1 || report.ccuTlvRefCount != 2) { + std::cerr << "tlv refcount mismatch init=" << g_initCalls + << " request=" << g_requestCalls + << " refs=" << report.ccuTlvRefCount << "\n"; + return 2; + } + second.Unload(); + if (g_deinitCalls != 0 || g_requestCalls != 1) { + std::cerr << "tlv released before final reference\n"; + return 3; + } + first.Unload(); + if (g_deinitCalls != 1 || g_requestCalls != 2) { + std::cerr << "tlv final release mismatch deinit=" << g_deinitCalls + << " request=" << g_requestCalls << "\n"; + return 4; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_hccp_loader_rolls_back_tlv_handle_when_ccu_init_request_fails(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_hccp_loader.h" + #undef private + + #include + + using namespace TileXR; + + int g_initCalls = 0; + int g_requestCalls = 0; + int g_deinitCalls = 0; + + int FakeRaTlvInit(TileXRCcuTlvInitInfo*, uint32_t* bufferSize, void** tlvHandle) + { + ++g_initCalls; + *bufferSize = 1024; + *tlvHandle = reinterpret_cast(0x1000ULL); + return 0; + } + + int FakeRaTlvRequest(void*, uint32_t moduleType, TileXRCcuTlvMsg* sendMsg, TileXRCcuTlvMsg*) + { + ++g_requestCalls; + if (moduleType != TILEXR_CCU_TLV_MODULE_TYPE_CCU || + sendMsg == nullptr || sendMsg->type != TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT) { + return -77; + } + return 128308; + } + + int FakeRaTlvDeinit(void*) + { + ++g_deinitCalls; + return 0; + } + + int main() + { + TileXRCcuHccpLoader loader; + loader.loaded_ = true; + loader.RaTlvInit = FakeRaTlvInit; + loader.RaTlvRequest = FakeRaTlvRequest; + loader.RaTlvDeinit = FakeRaTlvDeinit; + + TileXRCcuHccpLoaderReport report; + const int ret = loader.InitCcuTlv(3, &report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "tlv request failure unexpectedly succeeded\n"; + return 1; + } + if (g_initCalls != 1 || g_requestCalls != 1 || g_deinitCalls != 1) { + std::cerr << "tlv rollback mismatch init=" << g_initCalls + << " request=" << g_requestCalls + << " deinit=" << g_deinitCalls << "\n"; + return 2; + } + if (report.ccuTlvInitialized || report.raTlvRequestRet != 128308 || + report.message.find("RaTlvRequest CCU_INIT failed ret=128308") == std::string::npos) { + std::cerr << "weak tlv failure diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + result = self.compile_and_run(code, extra_sources=[LOADER_SOURCE], extra_link_flags=["-ldl"]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_ccu_runtime_initializes_ra_hdc_after_resolving_device_phy_id(self): + header = DIRECT_RUNTIME_HEADER.read_text(encoding="utf-8") + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + types = TYPES_HEADER.read_text(encoding="utf-8") + loader_header = LOADER_HEADER.read_text(encoding="utf-8") + loader_source = LOADER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TileXRCcuRaInitConfig", types) + self.assertIn("TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2", types) + self.assertIn("TileXRCcuRaInitFunc", types) + self.assertIn("TileXRCcuRaDeinitFunc", types) + self.assertIn("RaInit", loader_header) + self.assertIn("RaDeinit", loader_header) + self.assertIn("InitRaHdc", loader_header) + self.assertIn('LoadSymbol(raHandle_, RaInit, "RaInit", nullptr)', loader_source) + self.assertIn('LoadSymbol(raHandle_, RaDeinit, "RaDeinit", nullptr)', loader_source) + self.assertIn("InitCcuTlv", loader_header) + self.assertIn('LoadOptionalSymbol(raHandle_, RaTlvInit, "RaTlvInit", nullptr)', loader_source) + self.assertIn('LoadOptionalSymbol(raHandle_, RaTlvRequest, "RaTlvRequest", nullptr)', loader_source) + self.assertIn('LoadOptionalSymbol(raHandle_, RaTlvDeinit, "RaTlvDeinit", nullptr)', loader_source) + self.assertIn("loader_.InitRaHdc", source) + self.assertIn("loader_.InitRaHdc(devicePhyId_, hdcType, true, &raReport)", source) + self.assertIn("loader_.InitCcuTlv(devicePhyId_, &tlvReport)", source) + self.assertIn("SelectDirectCcuHdcType", source) + self.assertIn("TILEXR_CCU_DIRECT_HDC_TYPE", source) + self.assertIn("raInitialized", header) + self.assertIn("ccuTlvInitialized", header) + + def test_direct_runtime_ra_init_failure_report_keeps_phy_and_hdc_context(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_runtime.h" + + #include + #include + + using namespace TileXR; + + int main() + { + setenv("TILEXR_CCU_DIRECT_HDC_TYPE", "6", 1); + + TileXRCcuDirectRuntime runtime; + TileXRCcuDirectRuntimeOptions options; + options.devId = 7; + options.rank = 0; + options.rankSize = 2; + TileXRCcuDirectRuntimeReport report; + const int ret = runtime.Init(options, &report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "runtime init unexpectedly succeeded\n"; + return 1; + } + if (report.logicDevId != 7 || report.devicePhyId != 0xbeefU || + report.hdcType != TILEXR_CCU_HDC_SERVICE_TYPE_RDMA || + report.raInitialized) { + std::cerr << "report lost init context logic=" << report.logicDevId + << " phy=" << report.devicePhyId + << " hdc=" << report.hdcType + << " ra=" << report.raInitialized << "\n"; + return 2; + } + if (report.message.find("RaInit failed") == std::string::npos) { + std::cerr << "missing RA failure diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + libra_code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_hccp_types.h" + + extern "C" int RaCustomChannel( + TileXR::TileXRCcuRaInfo, + TileXR::TileXRCcuCustomChannelIn*, + TileXR::TileXRCcuCustomChannelOut*) + { + return 0; + } + + extern "C" int RaInit(TileXR::TileXRCcuRaInitConfig*) + { + return -55; + } + + extern "C" int RaDeinit(TileXR::TileXRCcuRaInitConfig*) + { + return 0; + } + ''' + ) + runtime_code = textwrap.dedent( + r''' + #include + + extern "C" int rtGetDevicePhyIdByIndex(uint32_t logicDevId, uint32_t* phyId) + { + if (logicDevId != 7 || phyId == nullptr) { + return -1; + } + *phyId = 0xbeefU; + return 0; + } + + struct TileXRCcuRtProcExtParam { + const char *paramInfo; + uint64_t paramLen; + }; + + struct TileXRCcuRtNetServiceOpenArgs { + TileXRCcuRtProcExtParam *extParamList; + uint64_t extParamCnt; + }; + + extern "C" int rtOpenNetService(const TileXRCcuRtNetServiceOpenArgs*) + { + return 0; + } + + extern "C" int rtCloseNetService() + { + return 0; + } + ''' + ) + + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + libra_cpp = temp_path / "fake_libra.cpp" + runtime_cpp = temp_path / "fake_libruntime.cpp" + libra_so = temp_path / "libra.so" + runtime_so = temp_path / "libruntime.so" + libra_cpp.write_text(libra_code, encoding="utf-8") + runtime_cpp.write_text(runtime_code, encoding="utf-8") + common_flags = [ + compiler, + "-std=c++14", + "-shared", + "-fPIC", + "-I", + str(REPO_ROOT / "src" / "include"), + "-I", + str(REPO_ROOT / "src" / "comm"), + ] + subprocess.run( + [*common_flags, str(libra_cpp), "-o", str(libra_so)], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + subprocess.run( + [*common_flags, str(runtime_cpp), "-o", str(runtime_so)], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + DRIVER_SOURCE, + ], + extra_link_flags=["-ldl"], + extra_env={"LD_LIBRARY_PATH": str(temp_path)}, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_runtime_tlv_init_failure_report_keeps_ra_phy_and_hdc_context(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_runtime.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuDirectRuntime runtime; + TileXRCcuDirectRuntimeOptions options; + options.devId = 7; + options.rank = 0; + options.rankSize = 2; + TileXRCcuDirectRuntimeReport report; + const int ret = runtime.Init(options, &report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "runtime init unexpectedly succeeded\n"; + return 1; + } + if (report.logicDevId != 7 || report.devicePhyId != 0xbeefU || + report.hdcType != TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2 || + !report.raInitialized || report.ccuTlvInitialized) { + std::cerr << "report lost TLV failure context logic=" << report.logicDevId + << " phy=" << report.devicePhyId + << " hdc=" << report.hdcType + << " ra=" << report.raInitialized + << " tlv=" << report.ccuTlvInitialized << "\n"; + return 2; + } + if (report.message.find("RaTlvRequest CCU_INIT failed ret=128308") == std::string::npos) { + std::cerr << "missing TLV failure diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + libra_code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_hccp_types.h" + + extern "C" int RaCustomChannel( + TileXR::TileXRCcuRaInfo, + TileXR::TileXRCcuCustomChannelIn*, + TileXR::TileXRCcuCustomChannelOut*) + { + return 0; + } + + extern "C" int RaInit(TileXR::TileXRCcuRaInitConfig*) + { + return 0; + } + + extern "C" int RaDeinit(TileXR::TileXRCcuRaInitConfig*) + { + return 0; + } + + extern "C" int RaTlvInit(TileXR::TileXRCcuTlvInitInfo*, uint32_t* bufferSize, void** tlvHandle) + { + *bufferSize = 1024; + *tlvHandle = reinterpret_cast(0x1000ULL); + return 0; + } + + extern "C" int RaTlvRequest( + void*, + uint32_t, + TileXR::TileXRCcuTlvMsg* sendMsg, + TileXR::TileXRCcuTlvMsg*) + { + return sendMsg != nullptr && sendMsg->type == TileXR::TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT ? + 128308 : 0; + } + + extern "C" int RaTlvDeinit(void*) + { + return 0; + } + ''' + ) + runtime_code = textwrap.dedent( + r''' + #include + + extern "C" int rtGetDevicePhyIdByIndex(uint32_t logicDevId, uint32_t* phyId) + { + if (logicDevId != 7 || phyId == nullptr) { + return -1; + } + *phyId = 0xbeefU; + return 0; + } + + struct TileXRCcuRtProcExtParam { + const char *paramInfo; + uint64_t paramLen; + }; + + struct TileXRCcuRtNetServiceOpenArgs { + TileXRCcuRtProcExtParam *extParamList; + uint64_t extParamCnt; + }; + + extern "C" int rtOpenNetService(const TileXRCcuRtNetServiceOpenArgs*) + { + return 0; + } + + extern "C" int rtCloseNetService() + { + return 0; + } + ''' + ) + + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + libra_cpp = temp_path / "fake_libra.cpp" + runtime_cpp = temp_path / "fake_libruntime.cpp" + libra_so = temp_path / "libra.so" + runtime_so = temp_path / "libruntime.so" + libra_cpp.write_text(libra_code, encoding="utf-8") + runtime_cpp.write_text(runtime_code, encoding="utf-8") + common_flags = [ + compiler, + "-std=c++14", + "-shared", + "-fPIC", + "-I", + str(REPO_ROOT / "src" / "include"), + "-I", + str(REPO_ROOT / "src" / "comm"), + ] + subprocess.run( + [*common_flags, str(libra_cpp), "-o", str(libra_so)], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + subprocess.run( + [*common_flags, str(runtime_cpp), "-o", str(runtime_so)], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + DRIVER_SOURCE, + ], + extra_link_flags=["-ldl"], + extra_env={"LD_LIBRARY_PATH": str(temp_path)}, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_ccu_driver_adapter_keeps_dynamic_loading_in_ccu_hccp_loader(self): + combined = DRIVER_HEADER.read_text(encoding="utf-8") + "\n" + DRIVER_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "dlopen", + "dlsym", + "libra.so", + "libhcomm", + "libhccl_v2", + "#include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuHccpLoader loader; + TileXRCcuHccpLoaderReport report; + if (loader.LoadEndpointRouteProviderFromEnv(&report) != TILEXR_SUCCESS) { + std::cerr << "load provider failed: " << report.message << "\n"; + return 1; + } + if (loader.CollectLocalEndpointRoute == nullptr) { + std::cerr << "endpoint route provider function missing\n"; + return 2; + } + + TileXRCcuEndpointRouteProviderResourceWindow window; + window.addr = 0x10000000ULL; + window.bytes = 0x2000; + window.tokenId = 0x1234; + window.rawTokenId = 0x2234; + window.tokenValue = 0; + + TileXRCcuEndpointRouteProviderRoute route; + if (loader.CollectLocalEndpointRoute(0x55, &window, &route) != TILEXR_SUCCESS) { + std::cerr << "provider call failed\n"; + return 3; + } + if (!route.endpointRouteVerified || route.remoteEid[0] != 0xaa || + route.tpn != 0x10203 || route.doorbellVa != 0x1122334455667788ULL || + route.doorbellTokenId != 0x3456 || route.doorbellTokenValue != 0 || + route.sqDepth != 64) { + std::cerr << "route mismatch\n"; + return 4; + } + if (!report.endpointRouteProviderLoaded) { + std::cerr << "provider report did not record loaded state\n"; + return 5; + } + return 0; + } + ''' + ) + plugin_code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_hccp_loader.h" + #include "tilexr_types.h" + + #include + + using namespace TileXR; + + extern "C" int TileXRCcuCollectLocalEndpointRoute( + uint32_t devicePhyId, + const TileXRCcuEndpointRouteProviderResourceWindow* window, + TileXRCcuEndpointRouteProviderRoute* route) + { + if (devicePhyId != 0x55 || window == nullptr || route == nullptr || + window->addr != 0x10000000ULL || window->tokenId != 0x1234) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + std::memset(route, 0, sizeof(*route)); + route->remoteEid[0] = 0xaa; + route->tpn = 0x10203; + route->doorbellVa = 0x1122334455667788ULL; + route->doorbellTokenId = 0x3456; + route->doorbellTokenValue = 0; + route->sqDepth = 64; + route->endpointRouteVerified = true; + return TILEXR_SUCCESS; + } + ''' + ) + + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + plugin_cpp = temp_path / "tilexr_ccu_endpoint_route_provider.cpp" + plugin_so = temp_path / "tilexr_ccu_endpoint_route_provider.so" + plugin_cpp.write_text(plugin_code, encoding="utf-8") + plugin_build = subprocess.run( + [ + compiler, + "-std=c++14", + "-shared", + "-fPIC", + "-I", + str(REPO_ROOT / "src" / "include"), + "-I", + str(REPO_ROOT / "src" / "comm"), + str(plugin_cpp), + "-o", + str(plugin_so), + ], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + self.assertEqual(0, plugin_build.returncode, plugin_build.stdout + plugin_build.stderr) + result = self.compile_and_run( + code, + extra_sources=[LOADER_SOURCE], + extra_link_flags=["-ldl"], + extra_env={"TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER": str(plugin_so)}, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_runtime_uses_optional_tilexr_endpoint_route_provider_before_env(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + #include + #include + + using namespace TileXR; + + int EchoLocalExchangeAsPeer(const void* sendBuf, size_t sendBytes, void* recvBuf, void*) + { + if (sendBuf == nullptr || sendBytes != sizeof(TileXRCcuResourceWindowExchange)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const auto* local = static_cast(sendBuf); + auto* out = static_cast(recvBuf); + std::memset(out, 0, sizeof(TileXRCcuResourceWindowExchange) * 2); + out[0] = *local; + return TILEXR_SUCCESS; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x55; + runtime.options_.rank = 1; + runtime.options_.rankSize = 2; + runtime.options_.allGather = EchoLocalExchangeAsPeer; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_SUCCESS) { + std::cerr << "provider route was not collected: " << report.message << "\n"; + return 1; + } + + std::vector buffers; + if (runtime.ExportRemoteCcuRmaBuffers(&buffers) != TILEXR_SUCCESS) { + std::cerr << "remote export failed\n"; + return 2; + } + if (buffers.size() != 1 || !buffers[0].endpointRouteVerified || + buffers[0].remoteEid[0] != 0xaa || + buffers[0].tpn != 0x10203 || + buffers[0].doorbellVa != 0x1122334455667788ULL || + buffers[0].doorbellTokenId != 0x3456 || + buffers[0].doorbellTokenValue != 0 || + buffers[0].sqDepth != 64) { + std::cerr << "provider route was not exported\n"; + return 3; + } + return 0; + } + ''' + ) + plugin_code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_hccp_loader.h" + #include "tilexr_types.h" + + #include + + using namespace TileXR; + + extern "C" int TileXRCcuCollectLocalEndpointRoute( + uint32_t devicePhyId, + const TileXRCcuEndpointRouteProviderResourceWindow* window, + TileXRCcuEndpointRouteProviderRoute* route) + { + if (devicePhyId != 0x55 || window == nullptr || route == nullptr || + window->addr != 0x10000000ULL || window->tokenId != 0x1234) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + std::memset(route, 0, sizeof(*route)); + route->remoteEid[0] = 0xaa; + route->tpn = 0x10203; + route->doorbellVa = 0x1122334455667788ULL; + route->doorbellTokenId = 0x3456; + route->doorbellTokenValue = 0; + route->sqDepth = 64; + route->endpointRouteVerified = true; + return TILEXR_SUCCESS; + } + ''' + ) + + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + plugin_cpp = temp_path / "tilexr_ccu_endpoint_route_provider.cpp" + plugin_so = temp_path / "tilexr_ccu_endpoint_route_provider.so" + plugin_cpp.write_text(plugin_code, encoding="utf-8") + plugin_build = subprocess.run( + [ + compiler, + "-std=c++14", + "-shared", + "-fPIC", + "-I", + str(REPO_ROOT / "src" / "include"), + "-I", + str(REPO_ROOT / "src" / "comm"), + str(plugin_cpp), + "-o", + str(plugin_so), + ], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + ) + self.assertEqual(0, plugin_build.returncode, plugin_build.stdout + plugin_build.stderr) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp", + ], + extra_link_flags=["-ldl"], + extra_env={"TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER": str(plugin_so)}, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_runtime_collects_ra_ctx_loop_endpoint_route_before_env_fallback(self): + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE", source) + self.assertIn("ra_ctx_loop", source) + self.assertIn("CollectLocalEndpointRouteWithRaCtx(&route)", source) + + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + #include + #include + + using namespace TileXR; + + constexpr uint32_t kRawDbTokenId = 0x345600U; + constexpr uint32_t kTokenValue = 0x7799U; + constexpr uint64_t kTpHandle = 0xabcddcbaULL; + constexpr uint64_t kCcuResourceBase = 0x10000000ULL; + constexpr uint64_t kHcommWqeBasicBlockOffset = 0x1000000ULL; + constexpr uint64_t kHcommSqBufferSize = 256ULL * 1024ULL; + constexpr uint32_t kDirectLoopJettyCtxId = 0; + constexpr uint64_t kExpectedSqVa = + kCcuResourceBase + kHcommWqeBasicBlockOffset + + static_cast(kDirectLoopJettyCtxId) * kHcommSqBufferSize; + + int g_cqCreateCalls = 0; + int g_qpCreateCalls = 0; + int g_tpInfoCalls = 0; + int g_reqResultCalls = 0; + int g_qpImportCalls = 0; + int g_qpBindCalls = 0; + int g_qpUnbindCalls = 0; + int g_qpUnimportCalls = 0; + int g_qpDestroyCalls = 0; + int g_cqDestroyCalls = 0; + TileXRCcuHccpQpCreateAttr g_lastQpAttr {}; + TileXRCcuHccpGetTpCfg g_lastTpCfg {}; + TileXRCcuHccpQpImportInfo g_lastImportInfo {}; + + int FakeCqCreate(void* ctx, TileXRCcuHccpCqInfo* info, void** cqHandle) + { + if (ctx != reinterpret_cast(0x1000) || info == nullptr || cqHandle == nullptr) { + return -1; + } + if (info->in.depth != 64 || info->in.ub.mode != 2) { + return -2; + } + ++g_cqCreateCalls; + *cqHandle = reinterpret_cast(0x2000); + return 0; + } + + int FakeCqDestroy(void*, void* cqHandle) + { + if (cqHandle == reinterpret_cast(0x2000)) { + ++g_cqDestroyCalls; + } + return 0; + } + + int FakeQpCreate( + void* ctx, + TileXRCcuHccpQpCreateAttr* attr, + TileXRCcuHccpQpCreateInfo* info, + void** qpHandle) + { + if (ctx != reinterpret_cast(0x1000) || attr == nullptr || info == nullptr || + qpHandle == nullptr) { + return -3; + } + g_lastQpAttr = *attr; + if (attr->scqHandle != reinterpret_cast(0x2000) || + attr->rcqHandle != reinterpret_cast(0x2000) || + attr->sqDepth != 8 || + attr->rqDepth != TILEXR_CCU_HCCP_RQ_DEPTH_DEFAULT || + attr->transportMode != TILEXR_CCU_HCCP_TRANSPORT_MODE_RM || + attr->ub.mode != static_cast(TILEXR_CCU_HCCP_JETTY_MODE_CCU) || + attr->ub.jettyId != 1024 || + attr->ub.tokenIdHandle != reinterpret_cast(0x3000) || + attr->ub.tokenValue != kTokenValue || + attr->ub.flag.bs.shareJfr != 1 || + attr->ub.jfsFlag.bs.errorSuspend != 1 || + attr->ub.extMode.cstmFlag.bs.sqCstm != 1 || + attr->ub.extMode.sqebbNum != 8 || + attr->ub.extMode.sq.buffVa != kExpectedSqVa || + attr->ub.extMode.sq.buffSize != 8 * 4 * 64) { + return -4; + } + for (uint32_t i = 0; i < TILEXR_CCU_HCCP_QP_KEY_BYTES; ++i) { + info->key.value[i] = static_cast(0x80U + i); + } + info->key.size = TILEXR_CCU_HCCP_QP_KEY_BYTES; + info->ub.dbAddr = 0x1122334455667788ULL; + info->ub.dbTokenId = kRawDbTokenId; + ++g_qpCreateCalls; + *qpHandle = reinterpret_cast(0x4000); + return 0; + } + + int FakeQpDestroy(void* qpHandle) + { + if (qpHandle == reinterpret_cast(0x4000)) { + ++g_qpDestroyCalls; + } + return 0; + } + + int FakeGetTpInfoListAsync( + void* ctx, + TileXRCcuHccpGetTpCfg* cfg, + TileXRCcuHccpTpInfo infoList[], + uint32_t* num, + void** reqHandle) + { + if (ctx != reinterpret_cast(0x1000) || cfg == nullptr || infoList == nullptr || + num == nullptr || reqHandle == nullptr || *num != 1) { + return -5; + } + g_lastTpCfg = *cfg; + if (cfg->flag.bs.rtp != 1 || cfg->transMode != TILEXR_CCU_HCCP_TRANSPORT_MODE_RM || + std::memcmp(cfg->localEid.raw, cfg->peerEid.raw, TILEXR_CCU_EID_BYTES) != 0) { + return -6; + } + infoList[0].tpHandle = kTpHandle; + ++g_tpInfoCalls; + *reqHandle = reinterpret_cast(0x5000); + return 0; + } + + int FakeGetAsyncReqResult(void* reqHandle, int* reqResult) + { + if (reqHandle != reinterpret_cast(0x5000) || reqResult == nullptr) { + return -7; + } + *reqResult = 0; + ++g_reqResultCalls; + return 0; + } + + int FakeQpImport(void* ctx, TileXRCcuHccpQpImportInfo* info, void** remoteQpHandle) + { + if (ctx != reinterpret_cast(0x1000) || info == nullptr || remoteQpHandle == nullptr) { + return -8; + } + g_lastImportInfo = *info; + if (info->in.key.size != TILEXR_CCU_HCCP_QP_KEY_BYTES || + info->in.key.value[0] != 0x80 || + info->in.ub.mode != TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP || + info->in.ub.tokenValue != kTokenValue || + info->in.ub.policy != TILEXR_CCU_HCCP_JETTY_GRP_POLICY_RR || + info->in.ub.type != TILEXR_CCU_HCCP_TARGET_TYPE_JETTY || + info->in.ub.flag.bs.tokenPolicy != TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT || + info->in.ub.expImportCfg.tpHandle != kTpHandle || + info->in.ub.expImportCfg.peerTpHandle != kTpHandle || + info->in.ub.expImportCfg.txPsn == 0 || + info->in.ub.expImportCfg.rxPsn == 0 || + info->in.ub.tpType != TILEXR_CCU_HCCP_TP_TYPE_RTP) { + return -9; + } + info->out.ub.tpn = 0; + ++g_qpImportCalls; + *remoteQpHandle = reinterpret_cast(0x6000); + return 0; + } + + int FakeQpUnimport(void*, void* remoteQpHandle) + { + if (remoteQpHandle == reinterpret_cast(0x6000)) { + ++g_qpUnimportCalls; + } + return 0; + } + + int FakeQpBind(void* qpHandle, void* remoteQpHandle) + { + if (qpHandle != reinterpret_cast(0x4000) || + remoteQpHandle != reinterpret_cast(0x6000)) { + return -10; + } + ++g_qpBindCalls; + return -11; + } + + int FakeQpUnbind(void* qpHandle) + { + if (qpHandle == reinterpret_cast(0x4000)) { + ++g_qpUnbindCalls; + } + return 0; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x55; + runtime.options_.rank = 0; + runtime.options_.rankSize = 1; + runtime.localResourceWindow_.addr = kCcuResourceBase; + runtime.localResourceWindow_.bytes = TILEXR_CCU_RESOURCE_WINDOW_BYTES; + runtime.localResourceWindow_.tokenId = 0x3456; + runtime.localResourceWindow_.rawTokenId = 0x345600; + runtime.localResourceWindow_.tokenValue = kTokenValue; + runtime.localResourceWindow_.raCtxHandle = reinterpret_cast(0x1000); + runtime.localResourceWindow_.tokenIdHandle = reinterpret_cast(0x3000); + runtime.localResourceWindow_.raCtxRegistered = true; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + runtime.localResourceWindow_.eid[i] = static_cast(0xa0U + i); + } + + runtime.loader_.RaCtxCqCreate = FakeCqCreate; + runtime.loader_.RaCtxCqDestroy = FakeCqDestroy; + runtime.loader_.RaCtxQpCreate = FakeQpCreate; + runtime.loader_.RaCtxQpDestroy = FakeQpDestroy; + runtime.loader_.RaGetTpInfoListAsync = FakeGetTpInfoListAsync; + runtime.loader_.RaGetAsyncReqResult = FakeGetAsyncReqResult; + runtime.loader_.RaCtxQpImport = FakeQpImport; + runtime.loader_.RaCtxQpUnimport = FakeQpUnimport; + runtime.loader_.RaCtxQpBind = FakeQpBind; + runtime.loader_.RaCtxQpUnbind = FakeQpUnbind; + + TileXRCcuDirectRuntimeReport report; + if (runtime.RefreshLocalVerifiedEndpointRoute(&report) != TILEXR_SUCCESS) { + std::cerr << "ra ctx loop route collection failed: " << report.message << "\n"; + return 1; + } + if (!runtime.localVerifiedEndpointRouteValid_) { + std::cerr << "verified route flag not set\n"; + return 2; + } + const auto& route = runtime.localVerifiedEndpointRoute_; + if (!route.endpointRouteVerified || + route.remoteEid[0] != 0xa0 || + route.tpn != 0 || + route.doorbellVa != 0x1122334455667788ULL || + route.doorbellTokenId != (kRawDbTokenId >> 8) || + route.doorbellTokenValue != kTokenValue || + route.sqDepth != 8) { + std::cerr << "verified route mismatch\n"; + return 3; + } + if (g_cqCreateCalls != 1 || g_qpCreateCalls != 1 || g_tpInfoCalls != 1 || + g_reqResultCalls != 1 || g_qpImportCalls != 1 || g_qpBindCalls != 0) { + std::cerr << "unexpected RA call counts\n"; + return 4; + } + + runtime.Shutdown(); + if (g_qpUnbindCalls != 0 || g_qpUnimportCalls != 1 || + g_qpDestroyCalls != 1 || g_cqDestroyCalls != 1) { + std::cerr << "endpoint route handles were not released\n"; + return 5; + } + return 0; + } + ''' + ) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp", + ], + extra_link_flags=["-ldl"], + extra_env={ + "TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE": "ra_ctx_loop", + }, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_runtime_retries_ra_ctx_endpoint_route_after_async_result_failure(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + #include + #include + + using namespace TileXR; + + constexpr uint32_t kRawDbTokenId = 0x456700U; + constexpr uint32_t kTokenValue = 0x8811U; + constexpr uint64_t kCcuResourceBase = 0x11000000ULL; + + int g_cqCreateCalls = 0; + int g_qpCreateCalls = 0; + int g_tpInfoCalls = 0; + int g_reqResultCalls = 0; + int g_qpImportCalls = 0; + int g_qpDestroyCalls = 0; + int g_cqDestroyCalls = 0; + + int FakeCqCreate(void*, TileXRCcuHccpCqInfo*, void** cqHandle) + { + if (cqHandle == nullptr) { + return -1; + } + ++g_cqCreateCalls; + *cqHandle = reinterpret_cast(0x2000 + g_cqCreateCalls); + return 0; + } + + int FakeCqDestroy(void*, void* cqHandle) + { + if (cqHandle != nullptr) { + ++g_cqDestroyCalls; + } + return 0; + } + + int FakeQpCreate( + void*, + TileXRCcuHccpQpCreateAttr*, + TileXRCcuHccpQpCreateInfo* info, + void** qpHandle) + { + if (info == nullptr || qpHandle == nullptr) { + return -2; + } + ++g_qpCreateCalls; + for (uint32_t i = 0; i < TILEXR_CCU_HCCP_QP_KEY_BYTES; ++i) { + info->key.value[i] = static_cast(0x40U + i); + } + info->key.size = TILEXR_CCU_HCCP_QP_KEY_BYTES; + info->ub.dbAddr = 0x1222333444555666ULL; + info->ub.dbTokenId = kRawDbTokenId; + *qpHandle = reinterpret_cast(0x4000 + g_qpCreateCalls); + return 0; + } + + int FakeQpDestroy(void* qpHandle) + { + if (qpHandle != nullptr) { + ++g_qpDestroyCalls; + } + return 0; + } + + int FakeGetTpInfoListAsync( + void*, + TileXRCcuHccpGetTpCfg*, + TileXRCcuHccpTpInfo infoList[], + uint32_t* num, + void** reqHandle) + { + if (infoList == nullptr || num == nullptr || reqHandle == nullptr || *num != 1) { + return -3; + } + ++g_tpInfoCalls; + infoList[0].tpHandle = 0xabcd0000ULL + g_tpInfoCalls; + *reqHandle = reinterpret_cast(0x5000 + g_tpInfoCalls); + return 0; + } + + int FakeGetAsyncReqResult(void*, int* reqResult) + { + if (reqResult == nullptr) { + return -4; + } + ++g_reqResultCalls; + *reqResult = (g_reqResultCalls <= 3) ? 528101 : 0; + return 0; + } + + int FakeQpImport(void*, TileXRCcuHccpQpImportInfo* info, void** remoteQpHandle) + { + if (info == nullptr || remoteQpHandle == nullptr) { + return -5; + } + ++g_qpImportCalls; + info->out.ub.tpn = 0x7000 + g_qpImportCalls; + *remoteQpHandle = reinterpret_cast(0x6000 + g_qpImportCalls); + return 0; + } + + int FakeQpUnimport(void*, void*) + { + return 0; + } + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x55; + runtime.options_.rank = 0; + runtime.options_.rankSize = 1; + runtime.localResourceWindow_.addr = kCcuResourceBase; + runtime.localResourceWindow_.bytes = TILEXR_CCU_RESOURCE_WINDOW_BYTES; + runtime.localResourceWindow_.tokenId = 0x4567; + runtime.localResourceWindow_.rawTokenId = kRawDbTokenId; + runtime.localResourceWindow_.tokenValue = kTokenValue; + runtime.localResourceWindow_.raCtxHandle = reinterpret_cast(0x1000); + runtime.localResourceWindow_.tokenIdHandle = reinterpret_cast(0x3000); + runtime.localResourceWindow_.raCtxRegistered = true; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + runtime.localResourceWindow_.eid[i] = static_cast(0xb0U + i); + } + + runtime.loader_.RaCtxCqCreate = FakeCqCreate; + runtime.loader_.RaCtxCqDestroy = FakeCqDestroy; + runtime.loader_.RaCtxQpCreate = FakeQpCreate; + runtime.loader_.RaCtxQpDestroy = FakeQpDestroy; + runtime.loader_.RaGetTpInfoListAsync = FakeGetTpInfoListAsync; + runtime.loader_.RaGetAsyncReqResult = FakeGetAsyncReqResult; + runtime.loader_.RaCtxQpImport = FakeQpImport; + runtime.loader_.RaCtxQpUnimport = FakeQpUnimport; + + TileXRCcuDirectRuntimeReport report; + const int ret = runtime.RefreshLocalVerifiedEndpointRoute(&report); + if (ret != TILEXR_SUCCESS) { + std::cerr << "ra ctx endpoint route retry failed: " << report.message + << " ret=" << ret << "\n"; + return 1; + } + if (!runtime.localVerifiedEndpointRouteValid_) { + std::cerr << "verified endpoint route flag not set after retry\n"; + return 2; + } + const auto& route = runtime.localVerifiedEndpointRoute_; + if (!route.endpointRouteVerified || + route.remoteEid[0] != 0xb0 || + route.doorbellVa != 0x1222333444555666ULL || + route.doorbellTokenId != (kRawDbTokenId >> 8) || + route.doorbellTokenValue != kTokenValue || + route.sqDepth != 8) { + std::cerr << "verified endpoint route mismatch after retry\n"; + return 3; + } + if (g_cqCreateCalls != 4 || g_qpCreateCalls != 4 || g_tpInfoCalls != 4 || + g_reqResultCalls != 4 || g_qpImportCalls != 1) { + std::cerr << "unexpected retry call counts" + << " cqCreate=" << g_cqCreateCalls + << " qpCreate=" << g_qpCreateCalls + << " tpInfo=" << g_tpInfoCalls + << " reqResult=" << g_reqResultCalls + << " qpImport=" << g_qpImportCalls << "\n"; + return 4; + } + if (g_qpDestroyCalls != 3 || g_cqDestroyCalls != 3) { + std::cerr << "failed attempt was not cleaned up before retry" + << " qpDestroy=" << g_qpDestroyCalls + << " cqDestroy=" << g_cqDestroyCalls << "\n"; + return 5; + } + + runtime.Shutdown(); + if (g_qpDestroyCalls != 4 || g_cqDestroyCalls != 4) { + std::cerr << "successful retry handles were not released on shutdown" + << " qpDestroy=" << g_qpDestroyCalls + << " cqDestroy=" << g_cqDestroyCalls << "\n"; + return 6; + } + return 0; + } + ''' + ) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp", + ], + extra_link_flags=["-ldl"], + extra_env={ + "TILEXR_CCU_DIRECT_ENDPOINT_ROUTE_COLLECTION_MODE": "ra_ctx_loop", + }, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_direct_runtime_fails_closed_when_configured_endpoint_route_provider_cannot_load(self): + code = textwrap.dedent( + r''' + #define private public + #include "ccu/tilexr_ccu_direct_runtime.h" + #undef private + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuDirectRuntime runtime; + runtime.initialized_ = true; + runtime.resourceWindowRegistered_ = true; + runtime.devicePhyId_ = 0x55; + runtime.options_.rank = 1; + runtime.options_.rankSize = 2; + runtime.localResourceWindow_.addr = 0x10000000ULL; + runtime.localResourceWindow_.bytes = 0x2000; + runtime.localResourceWindow_.tokenId = 0x1234; + runtime.localResourceWindow_.rawTokenId = 0x2234; + runtime.localResourceWindow_.tokenValue = 0; + + TileXRCcuDirectRuntimeReport report; + const int ret = runtime.RefreshLocalVerifiedEndpointRoute(&report); + if (ret == TILEXR_SUCCESS) { + std::cerr << "configured bad provider fell back to env route\n"; + return 1; + } + if (runtime.localVerifiedEndpointRouteValid_) { + std::cerr << "bad provider left a verified route\n"; + return 2; + } + if (report.message.find("failed to load direct CCU endpoint route provider") == std::string::npos) { + std::cerr << "unexpected failure message: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + result = self.compile_and_run( + code, + extra_sources=[ + DIRECT_RUNTIME_SOURCE, + LOADER_SOURCE, + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp", + ], + extra_link_flags=["-ldl"], + extra_env={ + "TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER": "/tmp/tilexr_missing_endpoint_route_provider.so", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_EID_RANK1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_TPN_RANK1": "0x10203", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_VA_RANK1": "0x1122334455667788", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_ID_RANK1": "0x3456", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_DOORBELL_TOKEN_VALUE_RANK1": "0", + "TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_SQ_DEPTH_RANK1": "64", + }, + ) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_ra_custom_channel_provider.py b/tests/ccu/test_tilexr_ccu_ra_custom_channel_provider.py new file mode 100644 index 0000000..6052edf --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_ra_custom_channel_provider.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROVIDER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.h" +PROVIDER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_ra_custom_channel_provider.cpp" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" +SPECS_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuRaCustomChannelProviderTest(unittest.TestCase): + def compile_and_run(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "ra_provider_test.cpp" + test_bin = temp_path / "ra_provider_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(PROVIDER_SOURCE), + str(DRIVER_SOURCE), + str(SPECS_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def test_provider_adapts_ra_custom_channel_to_driver_adapter(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + + #include + + using namespace TileXR; + + void* g_tilexrCcuRaProviderTestState = nullptr; + + struct FakeRaState { + uint32_t phyId = 0; + uint32_t mode = 0; + uint32_t op = 0; + uint32_t die = 0; + }; + + int FakeRaCustomChannel( + TileXRCcuRaInfo info, + TileXRCcuCustomChannelIn* in, + TileXRCcuCustomChannelOut* out) + { + auto* state = static_cast(g_tilexrCcuRaProviderTestState); + state->phyId = info.phyId; + state->mode = info.mode; + state->op = in->op; + state->die = in->data.dataInfo.udieIdx; + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x66; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x0badcafeU; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x300000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (1U << 24) | (2U << 16) | 31U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (15U << 16) | 7U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (3U << 16) | 5U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (9U << 16) | 1U; + return 0; + } + + int main() + { + FakeRaState state; + g_tilexrCcuRaProviderTestState = &state; + + TileXRCcuRaCustomChannelProvider provider; + TileXRCcuRaCustomChannelProviderReport providerReport; + if (provider.Init(9, FakeRaCustomChannel, &providerReport) != TILEXR_SUCCESS) { + std::cerr << "provider init failed: " << providerReport.message << "\n"; + return 1; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + if (provider.CreateAdapter(&adapter, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "create adapter failed: " << adapterReport.message << "\n"; + return 2; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(1, &basic, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << adapterReport.message << "\n"; + return 3; + } + if (state.phyId != 9 || state.mode != TILEXR_CCU_NETWORK_OFFLINE || + state.op != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.die != 1) { + std::cerr << "RA call mismatch\n"; + return 4; + } + if (basic.missionKey != 0x0badcafeU || basic.resourceAddr != 0x300000000ULL || + basic.msId != 0x66) { + std::cerr << "basic info mismatch\n"; + return 5; + } + if (providerReport.message != "ok" || providerReport.devicePhyId != 9) { + std::cerr << "provider report mismatch\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_accepts_opaque_ra_custom_channel_c_abi_shape(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + + #include + + using namespace TileXR; + + void* g_tilexrCcuRaProviderTestState = nullptr; + + struct LegacyRaInfo { + int mode = 0; + uint32_t phyId = 0; + }; + + struct FakeRaState { + uint32_t phyId = 0; + uint32_t mode = 0; + uint32_t op = 0; + uint32_t die = 0; + }; + + int FakeOpaqueRaCustomChannel(LegacyRaInfo info, void* rawIn, void* rawOut) + { + auto* state = static_cast(g_tilexrCcuRaProviderTestState); + auto* in = static_cast(rawIn); + auto* out = static_cast(rawOut); + state->phyId = info.phyId; + state->mode = info.mode; + state->op = in->op; + state->die = in->data.dataInfo.udieIdx; + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x77; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x12345678U; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x400000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (1U << 24) | (2U << 16) | 31U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (15U << 16) | 7U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (3U << 16) | 5U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (9U << 16) | 1U; + return 0; + } + + int main() + { + FakeRaState state; + g_tilexrCcuRaProviderTestState = &state; + + TileXRCcuRaCustomChannelProvider provider; + TileXRCcuRaCustomChannelProviderReport providerReport; + if (provider.Init(13, FakeOpaqueRaCustomChannel, &providerReport) != TILEXR_SUCCESS) { + std::cerr << "provider init failed: " << providerReport.message << "\n"; + return 1; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + if (provider.CreateAdapter(&adapter, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "create adapter failed: " << adapterReport.message << "\n"; + return 2; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(2, &basic, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << adapterReport.message << "\n"; + return 3; + } + if (state.phyId != 13 || state.mode != TILEXR_CCU_NETWORK_OFFLINE || + state.op != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.die != 2) { + std::cerr << "RA call mismatch\n"; + return 4; + } + if (basic.missionKey != 0x12345678U || basic.resourceAddr != 0x400000000ULL || + basic.msId != 0x77) { + std::cerr << "basic info mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_is_wired_and_keeps_hcomm_runtime_out_of_ccu_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = PROVIDER_HEADER.read_text(encoding="utf-8") + source = PROVIDER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_ra_custom_channel_provider.h", cmake) + self.assertIn("ccu/tilexr_ccu_ra_custom_channel_provider.cpp", cmake) + self.assertIn("TileXRCcuRaCustomChannelProvider", header) + self.assertIn("CreateAdapter", header) + self.assertIn("TileXRCcuRaCustomChannelFunc", header) + self.assertIn("std::function", header) + self.assertIn("TILEXR_CCU_NETWORK_OFFLINE", source) + self.assertIn("TileXRCcuDriverAdapter", header) + self.assertNotIn("udma/", header) + + combined = header + "\n" + source + for needle in [ + "#include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << planReport.message << "\n"; + return 1; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + if (TileXRCcuBuildRepositoryImage(plan, program, &image, &report) != TILEXR_SUCCESS) { + std::cerr << "repository image build failed: " << report.message << "\n"; + return 2; + } + + if (image.repositoryStartId != 475 || image.repositoryCount != 170 || + image.missionStartId != 489 || image.missionCount != 156 || + image.missionOffset != 14 || image.sqeLoadOffset != 14 || + image.sqeLoadCount != 13 || image.syncOffset != 27 || image.syncCount != 11) { + std::cerr << "unexpected repository metadata\n"; + return 3; + } + if (image.instructions.size() != 170) { + std::cerr << "unexpected repository image size\n"; + return 4; + } + if (image.instructions[14].words[0] != 0x0000000007a90001ULL || + image.instructions[26].words[0] != 0x0000000c07b50001ULL || + image.instructions[27].words[0] != 0x0000000007a90001ULL || + image.instructions[28].words[0] != 0x0000000107aa0001ULL || + image.instructions[29].words[0] != 0x0000000007b60003ULL || + image.instructions[30].words[0] != 0x0000000007b60003ULL || + image.instructions[31].words[0] != 0x0001016c00000802ULL || + image.instructions[31].words[1] != 0 || + image.instructions[32].words[0] != 0x000007a90939100dULL || + image.instructions[32].words[1] != 0x00000001016c0002ULL || + image.instructions[32].words[2] != 0x0001000000000000ULL || + image.instructions[35].words[0] != 0x0000000000010802ULL || + image.instructions[35].words[1] != 0x000000000001016cULL) { + std::cerr << "unexpected installed instructions\n"; + return 5; + } + TileXRCcuInstr expectedNop; + if (TileXRCcuEncodeLoadImdToXn(plan.kernelLocalXn.startId, 0, 0, &expectedNop) != + TILEXR_SUCCESS) { + std::cerr << "failed to encode expected repository padding nop\n"; + return 6; + } + if (image.instructions[13].words[0] != expectedNop.words[0] || + image.instructions[13].words[1] != expectedNop.words[1] || + image.instructions[13].words[2] != expectedNop.words[2] || + image.instructions[13].words[3] != expectedNop.words[3] || + image.instructions[38].words[0] != expectedNop.words[0] || + image.instructions[38].words[1] != expectedNop.words[1] || + image.instructions[38].words[2] != expectedNop.words[2] || + image.instructions[38].words[3] != expectedNop.words[3]) { + std::cerr << "unused repository slots should contain valid nop padding\n"; + return 6; + } + if (report.repositoryCount != 170 || report.installedInstructionCount != 24 || + report.sqeLoadOffset != 14 || report.syncOffset != 27 || report.message != "ok") { + std::cerr << "unexpected repository report\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_image_uploads_mission_window_and_installs_via_driver_adapter(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_repository.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + uint64_t allocBytes = 0; + uint64_t copiedBytes = 0; + bool freed = false; + uint32_t observedOp = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint64_t observedResourceAddr = 0; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->allocBytes = bytes; + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + state->copiedBytes = bytes; + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOp = in.op; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedResourceAddr = in.data.dataInfo.dataArray[0].insinfo.resourceAddr; + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuProducerPlan plan = MakePlan(); + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << planReport.message << "\n"; + return 1; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + if (TileXRCcuBuildRepositoryImage(plan, program, &image, &report) != TILEXR_SUCCESS) { + std::cerr << "repository build failed: " << report.message << "\n"; + return 2; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 3; + } + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + + TileXRCcuRepositoryInstallReceipt receipt; + if (TileXRCcuInstallRepositoryImage( + image, memoryOps, &state, adapter, &receipt, &report) != TILEXR_SUCCESS) { + std::cerr << "repository install failed: " << report.message << "\n"; + return 4; + } + + const uint64_t expectedBytes = 156ULL * sizeof(TileXRCcuInstr); + if (state.allocBytes != expectedBytes || state.copiedBytes != expectedBytes || + receipt.instructionStartId != 489 || receipt.instructionCount != 156 || + receipt.instructionBytes != expectedBytes || !receipt.uploaded || !receipt.installed) { + std::cerr << "upload receipt mismatch\n"; + return 5; + } + if (state.observedOp != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.observedOffset != 489 || state.observedDataLen != expectedBytes || + state.observedResourceAddr != receipt.deviceInstructionAddr) { + std::cerr << "install adapter request mismatch\n"; + return 6; + } + + const auto* installed = reinterpret_cast(state.deviceBytes.data()); + if (installed[0].words[0] != image.instructions[image.missionOffset].words[0] || + installed[12].words[0] != image.instructions[image.missionOffset + 12].words[0] || + installed[13].words[0] != image.instructions[image.syncOffset].words[0]) { + std::cerr << "mission-window upload content mismatch\n"; + return 7; + } + if (report.installedInstructionCount != 156 || !report.repositoryInstalled || + report.message != "ok") { + std::cerr << "install report mismatch\n"; + return 8; + } + + if (TileXRCcuReleaseRepositoryInstallReceipt(receipt, memoryOps, &state, &report) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "release failed: " << report.message << "\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_install_options_can_upload_full_repository_with_descriptor_len(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_repository.h" + + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + uint64_t allocBytes = 0; + uint64_t copiedBytes = 0; + std::vector deviceBytes; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint64_t observedResourceAddr = 0; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->allocBytes = bytes; + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size()) { + return -1; + } + state->copiedBytes = bytes; + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedResourceAddr = in.data.dataInfo.dataArray[0].insinfo.resourceAddr; + out->opRet = 0; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuProducerPlan plan = MakePlan(); + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << planReport.message << "\n"; + return 1; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + if (TileXRCcuBuildRepositoryImage(plan, program, &image, &report) != TILEXR_SUCCESS) { + std::cerr << "repository build failed: " << report.message << "\n"; + return 2; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 3; + } + + TileXRCcuRepositoryInstallOptions options; + options.window = TileXRCcuRepositoryInstallWindow::FullRepository; + options.dataLenMode = TileXRCcuRepositoryInstallDataLenMode::DescriptorBytes; + + TileXRCcuDeviceMemoryOps memoryOps = {FakeAlloc, FakeCopy, FakeFree}; + TileXRCcuRepositoryInstallReceipt receipt; + if (TileXRCcuInstallRepositoryImageWithOptions( + image, options, memoryOps, &state, adapter, &receipt, &report) != TILEXR_SUCCESS) { + std::cerr << "repository install failed: " << report.message << "\n"; + return 4; + } + + const uint64_t expectedBytes = 170ULL * sizeof(TileXRCcuInstr); + if (state.allocBytes != expectedBytes || state.copiedBytes != expectedBytes || + receipt.instructionStartId != 475 || receipt.instructionCount != 170 || + receipt.instructionBytes != expectedBytes || !receipt.uploaded || !receipt.installed) { + std::cerr << "full repository receipt mismatch\n"; + return 5; + } + if (state.observedOffset != 475 || + state.observedDataLen != sizeof(TileXRCcuInstrInfo) || + state.observedResourceAddr != receipt.deviceInstructionAddr) { + std::cerr << "full repository SET_INSTRUCTION envelope mismatch\n"; + return 6; + } + const auto* installed = reinterpret_cast(state.deviceBytes.data()); + if (installed[0].words[0] != image.instructions[0].words[0] || + installed[14].words[0] != image.instructions[image.missionOffset].words[0]) { + std::cerr << "full repository upload content mismatch\n"; + return 7; + } + if (report.installedInstructionCount != 170 || + report.message.find("window=full_repository") == std::string::npos || + report.message.find("dataLenMode=descriptor_bytes") == std::string::npos) { + std::cerr << "install report missing option detail: " << report.message << "\n"; + return 8; + } + if (TileXRCcuReleaseRepositoryInstallReceipt(receipt, memoryOps, &state, &report) != + TILEXR_SUCCESS || !state.freed) { + std::cerr << "release failed: " << report.message << "\n"; + return 9; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_install_failure_reports_full_set_instruction_context(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_repository.h" + + #include + #include + #include + #include + + using namespace TileXR; + + struct FakeState { + std::vector deviceBytes; + uint64_t readbackBytes = 0; + uint64_t driverReadbackBytes = 0; + bool freed = false; + }; + + int FakeAlloc(uint64_t bytes, void** ptr, void* userData) + { + auto* state = static_cast(userData); + state->deviceBytes.assign(static_cast(bytes), 0); + *ptr = state->deviceBytes.data(); + return 0; + } + + int FakeCopy(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (dst != state->deviceBytes.data() || dstBytes != state->deviceBytes.size() || + bytes != dstBytes) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + return 0; + } + + int FakeCopyDeviceToHost(void* dst, uint64_t dstBytes, const void* src, uint64_t bytes, void* userData) + { + auto* state = static_cast(userData); + if (src != state->deviceBytes.data() || dstBytes < bytes || + bytes != state->deviceBytes.size()) { + return -1; + } + std::memcpy(dst, src, static_cast(bytes)); + state->readbackBytes = bytes; + return 0; + } + + int FakeFree(void* ptr, void* userData) + { + auto* state = static_cast(userData); + if (ptr != state->deviceBytes.data()) { + return -1; + } + state->freed = true; + state->deviceBytes.clear(); + return 0; + } + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + out->opRet = 0; + if (in.op == TILEXR_CCU_U_OP_SET_INSTRUCTION) { + return 328107; + } + if (in.op != TILEXR_CCU_U_OP_GET_INSTRUCTION) { + return -1; + } + auto* state = static_cast(userData); + constexpr uint32_t kBaseInstruction = 475; + const uint32_t count = in.data.dataInfo.dataArraySize; + if (count == 0 || count > TILEXR_CCU_MAX_DATA_ARRAY_SIZE || + in.offsetStartIdx < kBaseInstruction || + in.data.dataInfo.dataLen != count * TILEXR_CCU_INSTRUCTION_BYTES) { + return -2; + } + const uint64_t byteOffset = + static_cast(in.offsetStartIdx - kBaseInstruction) * + TILEXR_CCU_INSTRUCTION_BYTES; + const uint64_t bytes = static_cast(count) * TILEXR_CCU_INSTRUCTION_BYTES; + if (byteOffset + bytes > state->deviceBytes.size()) { + return -3; + } + for (uint32_t i = 0; i < count; ++i) { + std::memcpy( + out->data.dataInfo.dataArray[i].byte32.raw, + state->deviceBytes.data() + byteOffset + + static_cast(i) * TILEXR_CCU_INSTRUCTION_BYTES, + TILEXR_CCU_INSTRUCTION_BYTES); + } + out->data.dataInfo.dataArraySize = count; + out->data.dataInfo.dataLen = static_cast(bytes); + out->offsetNextIdx = in.offsetStartIdx + count; + state->driverReadbackBytes += bytes; + return 0; + } + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {0x100051152e00ULL}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + bool Has(const std::string& haystack, const std::string& needle) + { + return haystack.find(needle) != std::string::npos; + } + + int main() + { + TileXRCcuProducerPlan plan = MakePlan(); + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << planReport.message << "\n"; + return 1; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + if (TileXRCcuBuildRepositoryImage(plan, program, &image, &report) != TILEXR_SUCCESS) { + std::cerr << "repository build failed: " << report.message << "\n"; + return 2; + } + + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport driverReport; + if (adapter.Init(5, FakeCustomChannel, &state, &driverReport) != TILEXR_SUCCESS) { + std::cerr << "adapter init failed\n"; + return 3; + } + + TileXRCcuRepositoryInstallOptions options; + options.window = TileXRCcuRepositoryInstallWindow::FullRepository; + options.dataLenMode = TileXRCcuRepositoryInstallDataLenMode::InstructionBytes; + + TileXRCcuDeviceMemoryOps memoryOps; + memoryOps.alloc = FakeAlloc; + memoryOps.copyHostToDevice = FakeCopy; + memoryOps.free = FakeFree; + memoryOps.copyDeviceToHost = FakeCopyDeviceToHost; + TileXRCcuRepositoryInstallReceipt receipt; + const int ret = TileXRCcuInstallRepositoryImageWithOptions( + image, options, memoryOps, &state, adapter, &receipt, &report); + if (ret != TILEXR_ERROR_MKIRT || !state.freed || receipt.deviceInstructionPtr != nullptr || + state.readbackBytes != 5440ULL || state.driverReadbackBytes != 5440ULL) { + std::cerr << "install failure handling mismatch ret=" << ret + << " freed=" << state.freed + << " readbackBytes=" << state.readbackBytes + << " driverReadbackBytes=" << state.driverReadbackBytes << "\n"; + return 4; + } + + const std::string msg = report.message; + for (const char* needle : { + "failed to install CCU repository instruction image", + "CCU custom channel call failed op=251 driverRet=328107 opRet=0", + "dieId=1", + "installStartId=475", + "installCount=170", + "instructionBytes=5440", + "customChannelDataLen=5440", + "deviceInstructionAddr=0x", + "window=full_repository", + "dataLenMode=instruction_bytes", + "firstInstructionWords=", + "lastInstructionWords=", + "instructionFnv1a64=0x", + "uploadReadback=ok", + "uploadReadbackBytes=5440", + "uploadReadbackFnv1a64=0x", + "uploadReadbackFirstInstructionWords=", + "uploadReadbackLastInstructionWords=", + "uploadReadbackMismatchCount=0", + "driverReadback=ok", + "driverReadbackRet=0", + "driverReadbackBytes=5440", + "driverReadbackFnv1a64=0x", + "driverReadbackFirstInstructionWords=", + "driverReadbackLastInstructionWords=", + "driverReadbackMismatchCount=0"}) { + if (!Has(msg, needle)) { + std::cerr << "missing diagnostic field '" << needle << "' in: " << msg << "\n"; + return 5; + } + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_image_places_pure_barrier_sync_microcode_without_sqe_load(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_repository.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuProducerPlan plan; + plan.mission = {0, 1, 0x12345678U, true}; + plan.kernelLocalXn = {0, 1, 1}; + plan.kernelLocalCke = {0, 1, 1}; + plan.kernelLocalMission = {0, 1, 1}; + plan.instructionWindow = {0, 1, 2, 1, 2}; + plan.syncResources.push_back({0, 1, 2, 1, 1, 1, 1, 1, 1}); + plan.taskWindows.push_back({0, 1, 2, 13, {}}); + + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed: " << planReport.message << "\n"; + return 1; + } + if (!program.sqeLoad.empty() || program.sync.size() != 2) { + std::cerr << "pure barrier microcode size mismatch\n"; + return 2; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + if (TileXRCcuBuildRepositoryImage(plan, program, &image, &report) != TILEXR_SUCCESS) { + std::cerr << "repository image build failed: " << report.message << "\n"; + return 3; + } + if (image.repositoryStartId != 1 || image.repositoryCount != 2 || + image.missionStartId != 1 || image.missionCount != 2 || + image.missionOffset != 0 || image.sqeLoadCount != 0 || + image.syncOffset != 0 || image.syncCount != 2 || + image.instructions.size() != 2) { + std::cerr << "pure barrier repository metadata mismatch\n"; + return 4; + } + if (image.instructions[0].words[0] == 0 || + image.instructions[1].words[0] == 0 || + report.installedInstructionCount != 2 || + report.sqeLoadOffset != 0 || report.syncOffset != 0) { + std::cerr << "pure barrier repository content/report mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_image_rejects_inconsistent_windows(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_repository.h" + + #include + + using namespace TileXR; + + TileXRCcuProducerPlan MakePlan() + { + TileXRCcuProducerPlan plan; + plan.mission = {1, 6, 0x059b0f03U, true}; + plan.kernelLocalXn = {1, 1961, 62}; + plan.kernelLocalCke = {1, 332, 1}; + plan.kernelLocalMission = {1, 6, 1}; + plan.instructionWindow = {1, 475, 170, 489, 156}; + plan.syncResources.push_back({1, 1961, 2361, 364, 2, 3}); + plan.syncResources.push_back({1, 1962, 2362, 364, 3, 3}); + plan.syncResources.push_back({1, 1963, 2364, 364, 4, 3}); + plan.taskWindows.push_back({1, 489, 13, 13, {}}); + plan.taskWindows.push_back({1, 502, 143, 13, {}}); + return plan; + } + + int main() + { + TileXRCcuProducerPlan plan = MakePlan(); + TileXRCcuProgram program; + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuBuildMicrocode(plan, &program, &planReport) != TILEXR_SUCCESS) { + std::cerr << "microcode build failed\n"; + return 1; + } + + TileXRCcuRepositoryImage image; + TileXRCcuRepositoryReport report; + + TileXRCcuProducerPlan smallRepo = plan; + smallRepo.instructionWindow.repositoryCount = 15; + if (TileXRCcuBuildRepositoryImage(smallRepo, program, &image, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "small repository was accepted\n"; + return 2; + } + if (report.message.find("repository") == std::string::npos) { + std::cerr << "small repository diagnostic was weak: " << report.message << "\n"; + return 3; + } + + TileXRCcuProducerPlan shortSqeTask = plan; + shortSqeTask.taskWindows[0].instCnt = 12; + if (TileXRCcuBuildRepositoryImage(shortSqeTask, program, &image, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "short SQE task window was accepted\n"; + return 4; + } + if (report.message.find("SQE") == std::string::npos) { + std::cerr << "short SQE task diagnostic was weak: " << report.message << "\n"; + return 5; + } + + TileXRCcuProducerPlan shiftedSyncTask = plan; + shiftedSyncTask.taskWindows[1].instStartId = 501; + if (TileXRCcuBuildRepositoryImage(shiftedSyncTask, program, &image, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "misaligned sync task window was accepted\n"; + return 6; + } + if (report.message.find("sync") == std::string::npos) { + std::cerr << "misaligned sync diagnostic was weak: " << report.message << "\n"; + return 7; + } + + if (TileXRCcuBuildRepositoryImage(plan, program, nullptr, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "null output image was accepted\n"; + return 8; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_repository_layer_is_wired_and_has_no_private_hcomm_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = REPOSITORY_HEADER.read_text(encoding="utf-8") + source = REPOSITORY_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_repository.h", cmake) + self.assertIn("ccu/tilexr_ccu_repository.cpp", cmake) + self.assertIn("struct TileXRCcuRepositoryImage", header) + self.assertIn("struct TileXRCcuRepositoryReport", header) + self.assertIn("struct TileXRCcuDeviceMemoryOps", header) + self.assertIn("TileXRCcuCopyDeviceToHostFn", header) + self.assertIn("copyDeviceToHost", header) + self.assertIn("struct TileXRCcuRepositoryInstallReceipt", header) + self.assertIn("enum class TileXRCcuRepositoryMemoryAllocMode", header) + self.assertIn("TileXRCcuMakeAclModule3DeviceMemoryOps", header) + self.assertIn("RtHbm", header) + self.assertIn("TileXRCcuMakeRtHbmDeviceMemoryOps", header) + self.assertIn("TileXRCcuMakeRepositoryDeviceMemoryOps", header) + self.assertIn("TileXRCcuRepositoryInstallOptions", header) + self.assertIn("TileXRCcuBuildRepositoryImage", header) + self.assertIn("TileXRCcuInstallRepositoryImageWithOptions", header) + self.assertIn("TileXRCcuInstallRepositoryImage", header) + self.assertIn("TileXRCcuReleaseRepositoryInstallReceipt", header) + self.assertIn("TileXRCcuMakeAclDeviceMemoryOps", header) + self.assertIn("tilexr_ccu_producer_plan.h", header) + self.assertIn("missionOffset", header) + self.assertIn("sqeLoadOffset", header) + self.assertIn("syncOffset", header) + self.assertIn("#include ", source) + self.assertIn("#include ", source) + self.assertIn("aclrtMalloc", source) + self.assertIn("ACL_MEM_MALLOC_HUGE_FIRST", source) + self.assertIn("aclrtMallocWithCfg", source) + self.assertIn("ACL_RT_MEM_ATTR_MODULE_ID", source) + self.assertIn("ACL_MEM_TYPE_HIGH_BAND_WIDTH", source) + self.assertIn("TILEXR_CCU_ACL_MODULE3_ID", source) + self.assertIn("aclrtMemcpy", source) + self.assertIn("ACL_MEMCPY_HOST_TO_DEVICE", source) + self.assertIn("ACL_MEMCPY_DEVICE_TO_HOST", source) + self.assertIn("aclrtFree", source) + self.assertIn("rtMalloc", source) + self.assertIn("RT_MEMORY_HBM", source) + self.assertIn("rtMemcpy", source) + self.assertIn("RT_MEMCPY_HOST_TO_DEVICE", source) + self.assertIn("RT_MEMCPY_DEVICE_TO_HOST", source) + self.assertIn("rtFree", source) + self.assertIn("TileXRCcuMakeAclDeviceMemoryOps", source) + self.assertIn("TileXRCcuMakeAclModule3DeviceMemoryOps", source) + self.assertIn("TileXRCcuMakeRtHbmDeviceMemoryOps", source) + self.assertIn("TileXRCcuMakeRepositoryDeviceMemoryOps", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_repository_install_failure_diagnostic_source_contract(self): + source = REPOSITORY_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "BuildInstallFailureDiagnostic", + "dieId=", + "installStartId=", + "installCount=", + "instructionBytes=", + "customChannelDataLen=", + "deviceInstructionAddr=0x", + "window=", + "dataLenMode=", + "firstInstructionWords=", + "lastInstructionWords=", + "instructionFnv1a64=0x", + "uploadReadback=", + "uploadReadbackBytes=", + "uploadReadbackFnv1a64=0x", + "uploadReadbackFirstInstructionWords=", + "uploadReadbackLastInstructionWords=", + "uploadReadbackMismatchCount=", + "driverReadback=", + "driverReadbackRet=", + "driverReadbackBytes=", + "driverReadbackFnv1a64=0x", + "driverReadbackFirstInstructionWords=", + "driverReadbackLastInstructionWords=", + "driverReadbackMismatchCount=", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_resource_allocator.py b/tests/ccu/test_tilexr_ccu_resource_allocator.py new file mode 100644 index 0000000..17c105f --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_resource_allocator.py @@ -0,0 +1,917 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +ALLOCATOR_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_resource_allocator.h" +ALLOCATOR_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_resource_allocator.cpp" +PRODUCER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_producer_plan.cpp" +BARRIER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_barrier_program.cpp" +MICROCODE_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_microcode.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuResourceAllocatorTest(unittest.TestCase): + def compile_and_run(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "allocator_test.cpp" + test_bin = temp_path / "allocator_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(ALLOCATOR_SOURCE), + str(PRODUCER_SOURCE), + str(BARRIER_SOURCE), + str(MICROCODE_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def test_allocator_declares_and_uses_separate_mission_instruction_start(self): + header = ALLOCATOR_HEADER.read_text(encoding="utf-8") + source = ALLOCATOR_SOURCE.read_text(encoding="utf-8") + + self.assertIn("uint16_t missionInstructionStartId = 0;", header) + self.assertIn("missionInstructionStartId", source) + self.assertIn("missionInstructionStart", source) + self.assertIn("repositoryPrefixCount", source) + self.assertIn("result.repository.num", source) + self.assertIn("result.repository.startId", source) + self.assertIn("generated.instructionWindow = {", source) + + def test_allocator_builds_complete_tilexr_owned_producer_plan(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.gsaStartId = 510; + spec.gsaCount = 20; + spec.ckeStartId = 332; + spec.ckeCount = 32; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 3; + request.syncInstructionCount = 143; + request.bindingsPerSyncResource = 3; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (!plan.mission.installed || plan.mission.dieId != 1 || plan.mission.missionId != 6 || + plan.mission.key != 0x059b0f03U) { + std::cerr << "mission/key mismatch\n"; + return 3; + } + if (plan.kernelLocalMission.startId != 6 || plan.kernelLocalMission.num != 1 || + plan.kernelLocalXn.startId != 1961 || plan.kernelLocalXn.num != 14 || + plan.kernelLocalGsa.startId != 510 || plan.kernelLocalGsa.num != 1 || + plan.kernelLocalCke.startId != 332 || plan.kernelLocalCke.num != 3) { + std::cerr << "kernel local ranges mismatch\n"; + return 4; + } + if (plan.instructionWindow.repositoryStartId != 475 || + plan.instructionWindow.repositoryCount != 156 || + plan.instructionWindow.missionStartId != 475 || + plan.instructionWindow.missionCount != 156) { + std::cerr << "instruction window mismatch\n"; + return 5; + } + if (plan.syncResources.size() != 3 || plan.taskWindows.size() != 2) { + std::cerr << "resource/task count mismatch\n"; + return 6; + } + if (plan.syncResources[0].localXn != 1961 || plan.syncResources[0].remoteXn != 1975 || + plan.syncResources[0].notifyCke != 332 || plan.syncResources[0].channelId != 2 || + plan.syncResources[0].bindingCount != 3) { + std::cerr << "first sync resource mismatch\n"; + return 7; + } + if (plan.syncResources[2].localXn != 1963 || plan.syncResources[2].remoteXn != 1977 || + plan.syncResources[2].notifyCke != 334 || plan.syncResources[2].channelId != 4) { + std::cerr << "last sync resource mismatch\n"; + return 8; + } + if (plan.taskWindows[0].instStartId != 475 || plan.taskWindows[0].instCnt != 13 || + plan.taskWindows[0].argSize != 13 || + plan.taskWindows[1].instStartId != 488 || plan.taskWindows[1].instCnt != 143 || + plan.taskWindows[1].argSize != 13) { + std::cerr << "task window mismatch\n"; + return 9; + } + + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "generated plan invalid: " << planReport.message << "\n"; + return 10; + } + if (allocation.receiptId == 0 || allocation.packageProvider != "tilexr-hcomm-derived-resource-allocator" || + allocation.localXn.startId != 1961 || allocation.remoteXn.startId != 1975 || + allocation.localGsa.startId != 510 || allocation.localGsa.num != 1 || + allocation.notifyCke.startId != 332 || allocation.channels.startId != 2 || + allocation.channels.num != 3) { + std::cerr << "allocation receipt mismatch\n"; + return 11; + } + if (report.missionAllocated != 1 || report.localXnAllocated != 14 || + report.localGsaAllocated != 1 || + report.remoteXnAllocated != 3 || report.notifyCkeAllocated != 3 || + report.channelBindingsAllocated != 9 || report.repositoryAllocated != 156 || + report.message != "ok") { + std::cerr << "report mismatch\n"; + return 12; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_can_assign_distinct_local_wait_and_remote_notify_cke_ranges(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 332; + spec.ckeCount = 32; + spec.localWaitCkeStartId = 332; + spec.localWaitCkeCount = 8; + spec.remoteNotifyCkeStartId = 364; + spec.remoteNotifyCkeCount = 8; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 3; + request.syncInstructionCount = 143; + request.bindingsPerSyncResource = 3; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (allocation.localWaitCke.startId != 332 || allocation.localWaitCke.num != 3 || + allocation.remoteNotifyCke.startId != 364 || allocation.remoteNotifyCke.num != 3 || + allocation.notifyCke.startId != 364 || allocation.notifyCke.num != 3) { + std::cerr << "split CKE allocation ranges mismatch\n"; + return 3; + } + if (plan.kernelLocalCke.startId != 332 || plan.kernelLocalCke.num != 3) { + std::cerr << "kernel-local CKE should describe the local wait CKE range\n"; + return 4; + } + if (plan.syncResources.size() != 3 || + plan.syncResources[0].localWaitCke != 332 || + plan.syncResources[0].notifyCke != 364 || + plan.syncResources[1].localWaitCke != 333 || + plan.syncResources[1].notifyCke != 365 || + plan.syncResources[2].localWaitCke != 334 || + plan.syncResources[2].notifyCke != 366) { + std::cerr << "split CKE sync resources mismatch\n"; + return 5; + } + if (report.localWaitCkeAllocated != 3 || + report.remoteNotifyCkeAllocated != 3 || + report.notifyCkeAllocated != 3) { + std::cerr << "split CKE report mismatch\n"; + return 6; + } + + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "split CKE generated plan invalid: " << planReport.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_can_reserve_repository_prefix_before_mission_instruction_window(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.missionInstructionStartId = 489; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 332; + spec.ckeCount = 32; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 3; + request.syncInstructionCount = 143; + request.bindingsPerSyncResource = 3; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (plan.instructionWindow.repositoryStartId != 475 || + plan.instructionWindow.repositoryCount != 170 || + plan.instructionWindow.missionStartId != 489 || + plan.instructionWindow.missionCount != 156) { + std::cerr << "repository/mission instruction window mismatch\n"; + return 3; + } + if (plan.taskWindows.size() != 2 || + plan.taskWindows[0].instStartId != 489 || + plan.taskWindows[0].instCnt != 13 || + plan.taskWindows[1].instStartId != 502 || + plan.taskWindows[1].instCnt != 143) { + std::cerr << "mission task windows did not start at mission instruction window\n"; + return 4; + } + if (allocation.repository.startId != 475 || + allocation.repository.num != 170 || + report.repositoryAllocated != 170) { + std::cerr << "repository allocation/report did not include prefix\n"; + return 5; + } + + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "prefixed repository plan invalid: " << planReport.message << "\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_builds_pure_barrier_plan_without_sqe_load_task(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 0; + spec.missionKey = 0x12345678U; + spec.missionStartId = 1; + spec.missionCount = 1; + spec.instructionStartId = 1; + spec.instructionCount = 16; + spec.xnStartId = 1; + spec.xnCount = 8; + spec.ckeStartId = 1; + spec.ckeCount = 8; + spec.channelStartId = 1; + spec.channelCount = 2; + + TileXRCcuResourceRequest request; + request.sqeArgCount = 0; + request.syncResourceCount = 1; + request.syncInstructionCount = 2; + request.bindingsPerSyncResource = 1; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (plan.taskWindows.size() != 1) { + std::cerr << "pure barrier should have one sync task\n"; + return 3; + } + if (plan.taskWindows[0].instStartId != 1 || + plan.taskWindows[0].instCnt != 2 || + plan.taskWindows[0].argSize != TILEXR_CCU_SQE_ARGS_LEN) { + std::cerr << "pure barrier sync task mismatch\n"; + return 4; + } + if (plan.kernelLocalXn.startId != 1 || plan.kernelLocalXn.num != 1 || + allocation.localXn.startId != 1 || allocation.localXn.num != 1 || + allocation.remoteXn.startId != 2 || allocation.remoteXn.num != 1 || + allocation.repository.startId != 1 || allocation.repository.num != 2) { + std::cerr << "pure barrier allocation mismatch\n"; + return 5; + } + if (report.localXnAllocated != 1 || + report.remoteXnAllocated != 1 || + report.repositoryAllocated != 2) { + std::cerr << "pure barrier report mismatch\n"; + return 6; + } + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "pure barrier plan invalid: " << planReport.message << "\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_assigns_independent_source_cke_for_sync_cke_barrier_mode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 0x220; + spec.ckeCount = 16; + spec.localWaitCkeStartId = 0x220; + spec.localWaitCkeCount = 8; + spec.remoteNotifyCkeStartId = 0x330; + spec.remoteNotifyCkeCount = 8; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 1; + request.syncInstructionCount = 3; + request.bindingsPerSyncResource = 1; + request.barrierMode = TileXRCcuBarrierMode::SyncCke; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (plan.barrierMode != TileXRCcuBarrierMode::SyncCke || + allocation.localWaitCke.startId != 0x220 || allocation.localWaitCke.num != 1 || + allocation.sourceCke.startId != 0x221 || allocation.sourceCke.num != 1 || + plan.kernelLocalCke.startId != 0x220 || plan.kernelLocalCke.num != 2) { + std::cerr << "sync_cke CKE allocation ranges mismatch\n"; + return 3; + } + if (plan.syncResources.size() != 1 || + plan.syncResources[0].notifyCke != 0x330 || + plan.syncResources[0].localWaitCke != 0x220 || + plan.syncResources[0].sourceCke != 0x221 || + plan.syncResources[0].sourceCkeMask != 0xffff) { + std::cerr << "sync_cke sync resource CKE fields mismatch\n"; + return 4; + } + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "sync_cke generated plan invalid: " << planReport.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_accepts_synccke_post_only_with_source_cke_and_two_instructions(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 0x220; + spec.ckeCount = 16; + spec.localWaitCkeStartId = 0x220; + spec.localWaitCkeCount = 8; + spec.remoteNotifyCkeStartId = 0x330; + spec.remoteNotifyCkeCount = 8; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 1; + request.syncInstructionCount = 2; + request.bindingsPerSyncResource = 1; + request.barrierMode = TileXRCcuBarrierMode::SyncCkePostOnly; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (plan.barrierMode != TileXRCcuBarrierMode::SyncCkePostOnly || + allocation.sourceCke.startId != 0x221 || allocation.sourceCke.num != 1 || + plan.instructionWindow.repositoryCount != TILEXR_CCU_SQE_ARGS_LEN + 2) { + std::cerr << "sync_cke_post_only allocation mismatch\n"; + return 3; + } + if (plan.syncResources.size() != 1 || + plan.syncResources[0].sourceCke != 0x221 || + plan.syncResources[0].notifyCke != 0x330 || + plan.syncResources[0].localWaitCke != 0x220) { + std::cerr << "sync_cke_post_only resource fields mismatch\n"; + return 4; + } + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "sync_cke_post_only generated plan invalid: " << planReport.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_accepts_local_cke_post_only_with_one_instruction(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 0x220; + spec.ckeCount = 16; + spec.localWaitCkeStartId = 0x220; + spec.localWaitCkeCount = 8; + spec.remoteNotifyCkeStartId = 0x330; + spec.remoteNotifyCkeCount = 8; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = 0; + request.syncResourceCount = 1; + request.syncInstructionCount = 1; + request.bindingsPerSyncResource = 1; + request.barrierMode = TileXRCcuBarrierMode::LocalCkePostOnly; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "allocate failed: " << report.message << "\n"; + return 2; + } + + if (plan.barrierMode != TileXRCcuBarrierMode::LocalCkePostOnly || + plan.taskWindows.size() != 1 || + plan.taskWindows[0].instCnt != 1 || + plan.instructionWindow.repositoryCount != 1) { + std::cerr << "local_cke_post_only allocation mismatch\n"; + return 3; + } + TileXRCcuProducerPlanReport planReport; + if (TileXRCcuValidateProducerPlan(plan, &planReport) != TILEXR_SUCCESS) { + std::cerr << "local_cke_post_only generated plan invalid: " << planReport.message << "\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_rejects_resource_exhaustion_and_double_release(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 0; + spec.missionKey = 0x12345678U; + spec.missionStartId = 1; + spec.missionCount = 1; + spec.instructionStartId = 100; + spec.instructionCount = 32; + spec.xnStartId = 200; + spec.xnCount = 17; + spec.ckeStartId = 300; + spec.ckeCount = 2; + spec.channelStartId = 4; + spec.channelCount = 1; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 3; + request.syncInstructionCount = 11; + request.bindingsPerSyncResource = 1; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "resource exhaustion was accepted\n"; + return 2; + } + if (report.message.find("insufficient CKE resources") == std::string::npos) { + std::cerr << "weak exhaustion diagnostic: " << report.message << "\n"; + return 3; + } + + spec.ckeCount = 8; + spec.channelCount = 3; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "reinit failed\n"; + return 4; + } + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_SUCCESS) { + std::cerr << "second allocate failed: " << report.message << "\n"; + return 5; + } + if (allocator.Release(allocation.receiptId) != TILEXR_SUCCESS) { + std::cerr << "release failed\n"; + return 6; + } + if (allocator.Release(allocation.receiptId) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "double release was accepted\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_rejects_sync_instruction_window_too_small_for_barrier_program(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 332; + spec.ckeCount = 32; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 2; + request.syncInstructionCount = 3; + request.bindingsPerSyncResource = 1; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "undersized barrier sync instruction window was accepted\n"; + return 2; + } + if (report.message.find("barrier") == std::string::npos || + report.message.find("sync instruction") == std::string::npos) { + std::cerr << "weak barrier instruction diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_counts_hcomm_style_task1_prelude_for_two_task_sync_xn_programs(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_resource_allocator.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuResourceSpec spec; + spec.dieId = 1; + spec.missionKey = 0x059b0f03U; + spec.missionStartId = 6; + spec.missionCount = 2; + spec.instructionStartId = 475; + spec.instructionCount = 170; + spec.xnStartId = 1961; + spec.xnCount = 62; + spec.ckeStartId = 332; + spec.ckeCount = 32; + spec.channelStartId = 2; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = TILEXR_CCU_SQE_ARGS_LEN; + request.syncResourceCount = 2; + request.syncInstructionCount = 2; + request.bindingsPerSyncResource = 1; + request.barrierMode = TileXRCcuBarrierMode::SyncXnPostOnly; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "allocator init failed\n"; + return 1; + } + + TileXRCcuProducerPlan plan; + TileXRCcuResourceAllocation allocation; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &plan, &allocation, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "undersized hcomm-style task1 prelude window was accepted\n"; + return 2; + } + if (report.message.find("prelude") == std::string::npos || + report.message.find("sync instruction") == std::string::npos) { + std::cerr << "weak prelude instruction diagnostic: " << report.message << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_allocator_is_wired_and_does_not_reference_hcomm_runtime_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = ALLOCATOR_HEADER.read_text(encoding="utf-8") + source = ALLOCATOR_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_resource_allocator.h", cmake) + self.assertIn("ccu/tilexr_ccu_resource_allocator.cpp", cmake) + self.assertIn("TileXRCcuResourceAllocator", header) + self.assertIn("TileXRCcuResourceSpec", header) + self.assertIn("TileXRCcuResourceRequest", header) + self.assertIn("TileXRCcuResourceAllocation", header) + self.assertIn("TileXRCcuProducerPlan", header) + self.assertIn("tilexr-hcomm-derived-resource-allocator", source) + self.assertIn("requiredBarrierInstructionCount", source) + self.assertIn("syncResourceCount * 2U", source) + self.assertIn("TileXRCcuBarrierMode::LocalCkePostOnly", source) + self.assertIn("request.barrierMode == TileXRCcuBarrierMode::LocalCkePostOnly", source) + self.assertIn("barrier sync instruction window is too small", source) + self.assertIn("const uint32_t channelCount = request.syncResourceCount", source) + self.assertNotIn("TILEXR_CCU_DIRECT_SYNC_RESOURCE_MAP", source) + self.assertNotIn("UseHcommTraceSyncResourceMap", source) + self.assertNotIn("TILEXR_CCU_DIRECT_SQE_LOAD_XN_MAP", source) + self.assertNotIn("TILEXR_CCU_DIRECT_TASK1_PRELUDE_CKE", source) + + combined = header + "\n" + source + for needle in [ + "#include ", source) + self.assertIn("static_assert(RT_CCU_SQE_ARGS_LEN == TILEXR_CCU_SQE_ARGS_LEN", source) + self.assertIn("rtCcuTaskInfo_t runtimeTask", source) + self.assertIn("rtCCULaunch(&runtimeTask, stream)", source) + self.assertIn("stream == nullptr", source) + self.assertIn("RT_CCU_INST_CNT_INVALID", source) + self.assertIn("RT_CCU_INST_START_MAX", source) + self.assertIn("task.argSize != 1 && task.argSize != TILEXR_CCU_SQE_ARGS_LEN", source) + self.assertNotIn("TILEXR_CCU_DIRECT_TASK_DIE_ID", source) + self.assertNotIn("TILEXR_CCU_DIRECT_TASK_TIMEOUT", source) + self.assertNotIn("TILEXR_CCU_DIRECT_TASK_ARG_SIZE", source) + self.assertNotIn("TILEXR_CCU_DIRECT_TASK_ARG", source) + self.assertNotIn("ApplyRuntimeTaskOverrides", source) + self.assertIn("TILEXR_ERROR_MKIRT", source) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_rt_ccu_launch_reference_is_confined_to_runtime_submit_wrapper(self): + offenders = [] + for root in [REPO_ROOT / "src" / "comm", REPO_ROOT / "src" / "include"]: + for path in root.rglob("*"): + if path.is_file() and path.suffix in {".h", ".hpp", ".cpp", ".cc", ".c"}: + text = path.read_text(encoding="utf-8", errors="replace") + if "rtCCULaunch" in text and path != CCU_SOURCE: + offenders.append(path.relative_to(REPO_ROOT).as_posix()) + self.assertEqual([], offenders) + + def test_runtime_kernel_header_is_confined_to_runtime_submit_wrapper(self): + offenders = [] + for root in [REPO_ROOT / "src" / "comm", REPO_ROOT / "src" / "include"]: + for path in root.rglob("*"): + if path.is_file() and path.suffix in {".h", ".hpp", ".cpp", ".cc", ".c"}: + text = path.read_text(encoding="utf-8", errors="replace") + if "runtime/kernel.h" in text and path != CCU_SOURCE: + offenders.append(path.relative_to(REPO_ROOT).as_posix()) + self.assertEqual([], offenders) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_runtime_fake_launch.py b/tests/ccu/test_tilexr_ccu_runtime_fake_launch.py new file mode 100644 index 0000000..d9d4c43 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_runtime_fake_launch.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +COMM_DIR = REPO_ROOT / "src" / "comm" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +RUNTIME_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_runtime.cpp" +ORCHESTRATOR_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_direct_orchestrator.cpp" +REPOSITORY_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_repository.cpp" +COMM_WRAP_SOURCE = COMM_DIR / "comm_wrap.cpp" + + +class TileXRCcuRuntimeFakeLaunchTest(unittest.TestCase): + def compile_and_run(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + runtime_dir = temp_path / "runtime" + runtime_dir.mkdir(parents=True) + acl_dir = temp_path / "acl" + acl_dir.mkdir(parents=True) + (acl_dir / "acl_rt.h").write_text( + textwrap.dedent( + r""" + #ifndef TILEXR_TEST_FAKE_ACL_RT_H + #define TILEXR_TEST_FAKE_ACL_RT_H + + #include + + #define ACL_SUCCESS 0 + #define ACL_MEM_MALLOC_HUGE_FIRST 0 + #define ACL_MEMCPY_HOST_TO_DEVICE 0 + #define ACL_MEMCPY_DEVICE_TO_HOST 1 + #define ACL_MEM_TYPE_HIGH_BAND_WIDTH 0 + #define ACL_RT_MEM_ATTR_MODULE_ID 0 + + typedef int aclError; + typedef int aclrtMemMallocPolicy; + typedef union aclrtMallocAttrValue { + unsigned int moduleId; + } aclrtMallocAttrValue; + typedef struct aclrtMallocAttribute { + int attr; + aclrtMallocAttrValue value; + } aclrtMallocAttribute; + typedef struct aclrtMallocConfig { + aclrtMallocAttribute* attrs; + size_t attrCount; + } aclrtMallocConfig; + + extern "C" aclError aclrtSetDevice(int deviceId); + extern "C" aclError aclrtMalloc(void** devPtr, size_t size, int policy); + static inline aclError aclrtMallocWithCfg( + void** devPtr, size_t size, int memoryType, aclrtMallocConfig* cfg) + { + (void)memoryType; + (void)cfg; + return aclrtMalloc(devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST); + } + extern "C" aclError aclrtMemcpy( + void* dst, size_t destMax, const void* src, size_t count, int kind); + extern "C" aclError aclrtFree(void* devPtr); + + #endif + """ + ), + encoding="utf-8", + ) + (runtime_dir / "kernel.h").write_text( + textwrap.dedent( + r""" + #ifndef TILEXR_TEST_FAKE_RUNTIME_KERNEL_H + #define TILEXR_TEST_FAKE_RUNTIME_KERNEL_H + + #include + + #define RT_CCU_SQE_ARGS_LEN 13U + #define RT_CCU_INST_CNT_INVALID 0U + #define RT_CCU_INST_START_MAX 65535U + #define RT_ERROR_NONE 0 + + typedef int32_t rtError_t; + typedef void* rtStream_t; + + typedef struct rtCcuTaskInfo { + uint8_t dieId; + uint8_t missionId; + uint16_t timeout; + uint16_t instStartId; + uint16_t instCnt; + uint32_t key; + uint32_t argSize; + uint64_t args[RT_CCU_SQE_ARGS_LEN]; + } rtCcuTaskInfo_t; + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t* taskInfo, rtStream_t stream); + + #endif + """ + ), + encoding="utf-8", + ) + + test_cpp = temp_path / "test.cpp" + test_cpp.write_text(code, encoding="utf-8") + test_bin = temp_path / "test_fake_launch" + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(temp_path), + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(RUNTIME_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def compile_and_run_public_prepared_handle(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + runtime_dir = temp_path / "runtime" + runtime_dir.mkdir(parents=True) + (runtime_dir / "kernel.h").write_text( + textwrap.dedent( + r""" + #ifndef TILEXR_TEST_FAKE_RUNTIME_KERNEL_H + #define TILEXR_TEST_FAKE_RUNTIME_KERNEL_H + + #include + + #define RT_CCU_SQE_ARGS_LEN 13U + #define RT_CCU_INST_CNT_INVALID 0U + #define RT_CCU_INST_START_MAX 65535U + #define RT_ERROR_NONE 0 + + typedef int32_t rtError_t; + typedef void* rtStream_t; + + typedef struct rtCcuTaskInfo { + uint8_t dieId; + uint8_t missionId; + uint16_t timeout; + uint16_t instStartId; + uint16_t instCnt; + uint32_t key; + uint32_t argSize; + uint64_t args[RT_CCU_SQE_ARGS_LEN]; + } rtCcuTaskInfo_t; + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t* taskInfo, rtStream_t stream); + + #endif + """ + ), + encoding="utf-8", + ) + acl_dir = temp_path / "acl" + acl_dir.mkdir(parents=True) + (acl_dir / "acl_rt.h").write_text( + textwrap.dedent( + r""" + #ifndef TILEXR_TEST_FAKE_ACL_RT_H + #define TILEXR_TEST_FAKE_ACL_RT_H + + #include + + #define ACL_SUCCESS 0 + #define ACL_MEM_MALLOC_HUGE_FIRST 0 + #define ACL_MEMCPY_HOST_TO_DEVICE 0 + #define ACL_MEMCPY_DEVICE_TO_HOST 1 + #define ACL_MEM_TYPE_HIGH_BAND_WIDTH 0 + #define ACL_RT_MEM_ATTR_MODULE_ID 0 + + typedef int aclError; + typedef int aclrtMemMallocPolicy; + typedef union aclrtMallocAttrValue { + unsigned int moduleId; + } aclrtMallocAttrValue; + typedef struct aclrtMallocAttribute { + int attr; + aclrtMallocAttrValue value; + } aclrtMallocAttribute; + typedef struct aclrtMallocConfig { + aclrtMallocAttribute* attrs; + size_t attrCount; + } aclrtMallocConfig; + + extern "C" aclError aclrtSetDevice(int deviceId); + extern "C" aclError aclrtMalloc(void** devPtr, size_t size, int policy); + static inline aclError aclrtMallocWithCfg( + void** devPtr, size_t size, int memoryType, aclrtMallocConfig* cfg) + { + (void)memoryType; + (void)cfg; + return aclrtMalloc(devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST); + } + extern "C" aclError aclrtMemcpy( + void* dst, size_t destMax, const void* src, size_t count, int kind); + extern "C" aclError aclrtFree(void* devPtr); + + #endif + """ + ), + encoding="utf-8", + ) + + test_cpp = temp_path / "test_public_prepared.cpp" + test_cpp.write_text(code, encoding="utf-8") + test_bin = temp_path / "test_public_prepared" + subprocess.run( + [ + compiler, + "-std=c++14", + "-DTILEXR_CCU_TESTING=1", + "-DTILEXR_LOG_DISABLE_SPDLOG=1", + "-ffunction-sections", + "-fdata-sections", + "-I", + str(temp_path), + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(COMM_WRAP_SOURCE), + str(ORCHESTRATOR_SOURCE), + str(REPOSITORY_SOURCE), + str(RUNTIME_SOURCE), + "-Wl,--gc-sections", + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def test_submit_task_copies_tilexr_fields_to_runtime_task_info(self): + code = textwrap.dedent( + r""" + #include "ccu/tilexr_ccu_runtime.h" + #include "tilexr_types.h" + + #include + #include + #include + + #include + + namespace { + rtCcuTaskInfo_t g_capturedTask {}; + rtStream_t g_capturedStream = nullptr; + int g_launchCount = 0; + } + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t* taskInfo, rtStream_t stream) + { + ++g_launchCount; + g_capturedStream = stream; + std::memcpy(&g_capturedTask, taskInfo, sizeof(g_capturedTask)); + return RT_ERROR_NONE; + } + + int main() + { + TileXR::TileXRCcuTask task {}; + task.dieId = 3; + task.missionId = 7; + task.timeout = 68; + task.instStartId = 1024; + task.instCnt = 13; + task.key = 0x05ab1234U; + task.argSize = TileXR::TILEXR_CCU_SQE_ARGS_LEN; + for (uint32_t i = 0; i < TileXR::TILEXR_CCU_SQE_ARGS_LEN; ++i) { + task.args[i] = 0x1000000000000000ULL + i; + } + + void* stream = reinterpret_cast(0x12345678ULL); + const int ret = TileXR::TileXRCcuSubmitTask(task, stream); + if (ret != TileXR::TILEXR_SUCCESS) { + std::printf("unexpected submit ret=%d\n", ret); + return 1; + } + if (g_launchCount != 1 || g_capturedStream != stream) { + std::printf("launchCount=%d capturedStream=%p\n", g_launchCount, g_capturedStream); + return 2; + } + if (g_capturedTask.dieId != task.dieId || g_capturedTask.missionId != task.missionId || + g_capturedTask.timeout != task.timeout || g_capturedTask.instStartId != task.instStartId || + g_capturedTask.instCnt != task.instCnt || g_capturedTask.key != task.key || + g_capturedTask.argSize != task.argSize) { + std::printf("runtime scalar field mismatch\n"); + return 3; + } + for (uint32_t i = 0; i < TileXR::TILEXR_CCU_SQE_ARGS_LEN; ++i) { + if (g_capturedTask.args[i] != task.args[i]) { + std::printf("arg[%u] mismatch\n", i); + return 4; + } + } + return 0; + } + """ + ) + + result = self.compile_and_run(code) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_submit_report_records_final_runtime_task(self): + code = textwrap.dedent( + r""" + #include "ccu/tilexr_ccu_runtime.h" + #include "tilexr_types.h" + + #include + #include + #include + + #include + + namespace { + rtCcuTaskInfo_t g_capturedTask {}; + } + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t* taskInfo, rtStream_t) + { + std::memcpy(&g_capturedTask, taskInfo, sizeof(g_capturedTask)); + return RT_ERROR_NONE; + } + + int main() + { + TileXR::TileXRCcuTask task {}; + task.dieId = 3; + task.missionId = 6; + task.timeout = 68; + task.instStartId = 489; + task.instCnt = 2; + task.key = 0x059b0f03U; + task.argSize = TileXR::TILEXR_CCU_SQE_ARGS_LEN; + task.args[0] = 0x1111ULL; + task.args[1] = 0x2222ULL; + + TileXR::TileXRCcuRuntimeSubmitReport report {}; + const int ret = TileXR::TileXRCcuSubmitTaskWithReport( + task, reinterpret_cast(0x1ULL), &report); + if (ret != TileXR::TILEXR_SUCCESS) { + std::printf("submit ret=%d\n", ret); + return 1; + } + if (!report.finalTaskCaptured) { + std::printf("final task was not captured\n"); + return 2; + } + if (report.finalTask.dieId != task.dieId || report.finalTask.timeout != task.timeout || + report.finalTask.argSize != task.argSize || report.finalTask.args[0] != 0x1111ULL || + report.finalTask.args[1] != 0x2222ULL) { + std::printf("final task mismatch die=%u timeout=%u argSize=%u arg0=0x%llx arg1=0x%llx\n", + static_cast(report.finalTask.dieId), + static_cast(report.finalTask.timeout), + static_cast(report.finalTask.argSize), + static_cast(report.finalTask.args[0]), + static_cast(report.finalTask.args[1])); + return 3; + } + if (g_capturedTask.dieId != report.finalTask.dieId || + g_capturedTask.timeout != report.finalTask.timeout || + g_capturedTask.argSize != report.finalTask.argSize || + g_capturedTask.args[0] != report.finalTask.args[0]) { + std::printf("captured runtime task differs from report\n"); + return 4; + } + return 0; + } + """ + ) + + result = self.compile_and_run(code) + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_submit_task_maps_runtime_launch_failure_to_mkirt_error(self): + code = textwrap.dedent( + r""" + #include "ccu/tilexr_ccu_runtime.h" + #include "tilexr_types.h" + + #include + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t*, rtStream_t) + { + return 507000; + } + + int main() + { + TileXR::TileXRCcuTask task {}; + task.dieId = 1; + task.missionId = 2; + task.instStartId = 8; + task.instCnt = 1; + task.key = 0x1234U; + task.argSize = 1; + task.args[0] = 0xfeedULL; + + const int ret = TileXR::TileXRCcuSubmitTask(task, reinterpret_cast(0x1ULL)); + return ret == TileXR::TILEXR_ERROR_MKIRT ? 0 : 1; + } + """ + ) + + result = self.compile_and_run(code) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_public_prepared_handle_submits_batch_through_runtime_launch(self): + code = textwrap.dedent( + r""" + #include "tilexr_api.h" + #include "tilexr_types.h" + + #include + #include + #include + + #include + #include + + extern "C" TileXRDirectCcuPreparedTasksPtr TileXRDirectCcuCreatePreparedForTest( + const TileXRDirectCcuTaskInfo* tasks, uint32_t taskCount); + + namespace { + rtCcuTaskInfo_t g_tasks[4] {}; + rtStream_t g_streams[4] {}; + int g_launchCount = 0; + int g_failOnCall = 0; + } + + extern "C" aclError aclrtSetDevice(int) { return ACL_SUCCESS; } + extern "C" aclError aclrtMalloc(void**, size_t, int) { return ACL_SUCCESS; } + extern "C" aclError aclrtMemcpy(void*, size_t, const void*, size_t, int) { return ACL_SUCCESS; } + extern "C" aclError aclrtFree(void*) { return ACL_SUCCESS; } + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t* taskInfo, rtStream_t stream) + { + const int index = g_launchCount++; + g_streams[index] = stream; + std::memcpy(&g_tasks[index], taskInfo, sizeof(g_tasks[index])); + return g_failOnCall == g_launchCount ? 507000 : RT_ERROR_NONE; + } + + TileXRDirectCcuTaskInfo MakeTask(uint8_t mission, uint16_t instStart) + { + TileXRDirectCcuTaskInfo task {}; + task.dieId = 2; + task.missionId = mission; + task.timeout = static_cast(30 + mission); + task.instStartId = instStart; + task.instCnt = 2; + task.key = 0xabc00000U + mission; + task.argSize = 13; + for (uint32_t i = 0; i < 13; ++i) { + task.args[i] = 0x8000000000000000ULL + (static_cast(mission) << 8U) + i; + } + return task; + } + + bool SameTask(const TileXRDirectCcuTaskInfo& expected, const rtCcuTaskInfo_t& actual) + { + if (expected.dieId != actual.dieId || expected.missionId != actual.missionId || + expected.timeout != actual.timeout || expected.instStartId != actual.instStartId || + expected.instCnt != actual.instCnt || expected.key != actual.key || + expected.argSize != actual.argSize) { + return false; + } + for (uint32_t i = 0; i < 13; ++i) { + if (expected.args[i] != actual.args[i]) { + return false; + } + } + return true; + } + + int main() + { + TileXRDirectCcuTaskInfo tasks[2] = {MakeTask(7, 101), MakeTask(8, 103)}; + TileXRDirectCcuPreparedTasksPtr prepared = TileXRDirectCcuCreatePreparedForTest(tasks, 2); + if (prepared == nullptr) { + std::printf("missing prepared handle\n"); + return 1; + } + + TileXRDirectCcuTaskInfo preview {}; + if (TileXRDirectCcuGetPreparedTask(prepared, 1, &preview) != TileXR::TILEXR_SUCCESS || + preview.missionId != tasks[1].missionId || preview.args[12] != tasks[1].args[12]) { + std::printf("prepared task preview mismatch\n"); + return 2; + } + + void* stream = reinterpret_cast(0x12345678ULL); + TileXRDirectCcuSubmitReport report {}; + const int ret = TileXRDirectCcuSubmitPrepared(prepared, stream, &report); + if (ret != TileXR::TILEXR_SUCCESS || !report.submitted || + report.taskCount != 2 || report.submittedTaskCount != 2) { + std::printf("submit report mismatch ret=%d submitted=%d taskCount=%u submittedTaskCount=%u\n", + ret, report.submitted ? 1 : 0, report.taskCount, report.submittedTaskCount); + return 3; + } + if (g_launchCount != 2 || g_streams[0] != stream || g_streams[1] != stream) { + std::printf("launch count or stream mismatch count=%d\n", g_launchCount); + return 4; + } + if (!SameTask(tasks[0], g_tasks[0]) || !SameTask(tasks[1], g_tasks[1])) { + std::printf("runtime task payload mismatch\n"); + return 5; + } + + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet == TileXR::TILEXR_SUCCESS ? 0 : 6; + } + """ + ) + + result = self.compile_and_run_public_prepared_handle(code) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_public_prepared_handle_reports_null_stream_and_mid_batch_failure(self): + code = textwrap.dedent( + r""" + #include "tilexr_api.h" + #include "tilexr_types.h" + + #include + #include + #include + + #include + #include + + extern "C" TileXRDirectCcuPreparedTasksPtr TileXRDirectCcuCreatePreparedForTest( + const TileXRDirectCcuTaskInfo* tasks, uint32_t taskCount); + + namespace { + int g_launchCount = 0; + int g_failOnCall = 0; + } + + extern "C" aclError aclrtSetDevice(int) { return ACL_SUCCESS; } + extern "C" aclError aclrtMalloc(void**, size_t, int) { return ACL_SUCCESS; } + extern "C" aclError aclrtMemcpy(void*, size_t, const void*, size_t, int) { return ACL_SUCCESS; } + extern "C" aclError aclrtFree(void*) { return ACL_SUCCESS; } + + extern "C" rtError_t rtCCULaunch(rtCcuTaskInfo_t*, rtStream_t) + { + ++g_launchCount; + return g_failOnCall == g_launchCount ? 507000 : RT_ERROR_NONE; + } + + TileXRDirectCcuTaskInfo MakeTask(uint8_t mission, uint16_t instStart) + { + TileXRDirectCcuTaskInfo task {}; + task.dieId = 1; + task.missionId = mission; + task.timeout = static_cast(60 + mission); + task.instStartId = instStart; + task.instCnt = static_cast(4 + mission); + task.key = 0x12340000U + mission; + task.argSize = 13; + for (uint32_t i = 0; i < 13; ++i) { + task.args[i] = 0xfeed000000000000ULL + (static_cast(mission) << 8U) + i; + } + return task; + } + + bool Contains(const char* text, const char* needle) + { + return text != nullptr && std::strstr(text, needle) != nullptr; + } + + int main() + { + TileXRDirectCcuTaskInfo tasks[2] = {MakeTask(2, 11), MakeTask(3, 12)}; + TileXRDirectCcuPreparedTasksPtr prepared = TileXRDirectCcuCreatePreparedForTest(tasks, 2); + if (prepared == nullptr) { + return 1; + } + + TileXRDirectCcuSubmitReport nullStreamReport {}; + int ret = TileXRDirectCcuSubmitPrepared(prepared, nullptr, &nullStreamReport); + if (ret != TileXR::TILEXR_ERROR_PARA_CHECK_FAIL || + nullStreamReport.submitted || nullStreamReport.taskCount != 2 || + nullStreamReport.submittedTaskCount != 0 || + !Contains(nullStreamReport.message, "missing runtime stream") || + g_launchCount != 0) { + std::printf("bad null-stream report ret=%d launchCount=%d message=%s\n", + ret, g_launchCount, nullStreamReport.message); + return 2; + } + + g_failOnCall = 2; + TileXRDirectCcuSubmitReport failReport {}; + ret = TileXRDirectCcuSubmitPrepared(prepared, reinterpret_cast(0x1ULL), &failReport); + if (ret != TileXR::TILEXR_ERROR_MKIRT || failReport.submitted || + failReport.taskCount != 2 || failReport.submittedTaskCount != 1 || + !Contains(failReport.message, "task=1") || + !Contains(failReport.message, "rtRet=507000") || + !Contains(failReport.message, "dieId=1") || + !Contains(failReport.message, "missionId=3") || + !Contains(failReport.message, "timeout=63") || + !Contains(failReport.message, "instStartId=12") || + !Contains(failReport.message, "instCnt=7") || + !Contains(failReport.message, "key=0x12340003") || + !Contains(failReport.message, "argSize=13") || + !Contains(failReport.message, "args[0]=0xfeed000000000300") || + !Contains(failReport.message, "args[12]=0xfeed00000000030c") || + g_launchCount != 2) { + std::printf("bad mid-batch report ret=%d launchCount=%d taskCount=%u submitted=%u message=%s\n", + ret, g_launchCount, failReport.taskCount, failReport.submittedTaskCount, + failReport.message); + return 3; + } + + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet == TileXR::TILEXR_SUCCESS ? 0 : 4; + } + """ + ) + + result = self.compile_and_run_public_prepared_handle(code) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_public_prepared_handle_test_seam_is_test_only_and_keeps_private_ccu_out(self): + source = COMM_WRAP_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TILEXR_CCU_TESTING", source) + self.assertIn("TileXRDirectCcuCreatePreparedForTest", source) + for needle in [ + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, source) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_source_guard_coverage.py b/tests/ccu/test_tilexr_ccu_source_guard_coverage.py new file mode 100644 index 0000000..2f45fd5 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_source_guard_coverage.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +SOURCE_GUARD = REPO_ROOT / "tests" / "comm" / "unit" / "test_tilexr_source_guards.cpp" +BUILD_ARTIFACT_GUARD = REPO_ROOT / "tests" / "ccu" / "check_tile_comm_no_hcomm_deps.sh" +DIRECT_SMOKE_PROBE = REPO_ROOT / "tests" / "ccu" / "ccu_tilexr_direct_smoke_probe.cpp" +DIRECT_SMOKE_RUNNER = REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_smoke.sh" +ROOT_CMAKE = REPO_ROOT / "CMakeLists.txt" +CCU_CMAKE = REPO_ROOT / "tests" / "ccu" / "CMakeLists.txt" + + +class TileXRCcuSourceGuardCoverageTest(unittest.TestCase): + def test_source_guard_covers_hccl_include_and_path_variants(self): + source = SOURCE_GUARD.read_text(encoding="utf-8").replace('\\"', '"') + + for needle in [ + "TestRootCMakeHcclIncludesAreNotTileCommSurface", + "#include ", + "#include \"hccl.h\"", + "pkg_inc/hccl", + "include/hccl", + "${ARCH}-linux/include/hccl", + "${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_root_cmake_does_not_expose_hccl_include_globally(self): + cmake = ROOT_CMAKE.read_text(encoding="utf-8") + + self.assertNotIn("${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl/", cmake) + self.assertNotIn("${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl", cmake) + + def test_ccu_cmake_does_not_define_private_probe_targets(self): + cmake = CCU_CMAKE.read_text(encoding="utf-8") + + for needle in [ + "TILEXR_BUILD_PRIVATE_CCU_PROBES", + "ccu_context_probe", + "ccu_barrier_kernel_probe", + "ccu_taskinfo_probe", + "include/hccl", + "pkg_inc/hcomm", + "hcomm", + "hccl", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, cmake) + + def test_build_artifact_guard_scans_private_hcomm_hccl_and_ccu_surfaces(self): + script = BUILD_ARTIFACT_GUARD.read_text(encoding="utf-8") + + for needle in [ + "readelf -d", + "ldd", + "nm -D", + "strings -a", + "libhcomm\\.so", + "libhccl_v2\\.so", + "libhccl_fwk\\.so", + "libmc2_client\\.so", + "HcclGetCcuTaskInfo", + "HcclChannelAcquire", + "HcommChannelNotify", + "RT_RES_TYPE_CCU_XN", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, script) + + def test_direct_smoke_probe_keeps_private_producer_dependencies_out(self): + source = DIRECT_SMOKE_PROBE.read_text(encoding="utf-8") + + for needle in [ + "#include ", + "#include \"hccl.h\"", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "CcuResBatchAllocator", + "CcuResRepository", + "runtime/kernel.h", + "rtCCULaunch", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, source) + + def test_direct_smoke_runner_keeps_private_link_dependencies_out(self): + source = DIRECT_SMOKE_RUNNER.read_text(encoding="utf-8") + + for needle in [ + "-lhcomm", + "-lhccl", + "-lhccl_v2", + "-lhccl_fwk", + "-lmc2_client", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "pkg_inc/hcomm", + "pkg_inc/hccl", + "include/hccl", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, source) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_specs.py b/tests/ccu/test_tilexr_ccu_specs.py new file mode 100644 index 0000000..4760eb9 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_specs.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +SPECS_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.h" +SPECS_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp" +ALLOCATOR_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_resource_allocator.h" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuSpecsTest(unittest.TestCase): + def compile_and_run(self, code: str): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "specs_test.cpp" + test_bin = temp_path / "specs_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(SPECS_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + return subprocess.run([str(test_bin)], cwd=REPO_ROOT, check=False, text=True, capture_output=True) + + def test_decodes_hcomm_basic_info_caps_into_tilexr_spec(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_specs.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x25; + basic.missionKey = 0x059b0f03U; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = (7U << 24) | (11U << 16) | 169U; + basic.caps.cap1 = (61U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 35U; + basic.caps.cap3 = (127U << 16) | 3U; + basic.caps.cap4 = 15U; + + TileXRCcuSpecInfo info; + TileXRCcuSpecsReport report; + if (TileXRCcuDecodeBasicInfo(basic, &info, &report) != TILEXR_SUCCESS) { + std::cerr << "decode failed: " << report.message << "\n"; + return 1; + } + + if (info.dieId != 1 || info.msId != 0x25 || info.missionKey != 0x059b0f03U || + info.resourceAddr != 0x100000000ULL) { + std::cerr << "base fields mismatch\n"; + return 2; + } + if (info.instructionNum != 170 || info.xnNum != 62 || info.gsaNum != 32 || + info.msNum != 64 || info.ckeNum != 36 || info.jettyNum != 128 || + info.channelNum != 4 || info.pfeNum != 16 || + info.missionNum != 12 || info.loopEngineNum != 8) { + std::cerr << "caps decode mismatch\n"; + return 3; + } + if (info.xnBaseAddr != 0x100000000ULL + TILEXR_CCU_V1_XN_RESOURCE_OFFSET) { + std::cerr << "xn base mismatch\n"; + return 4; + } + + TileXRCcuResourceSpec spec; + if (TileXRCcuBuildResourceSpec(info, 6, 475, 1961, 332, 2, &spec, &report, 510) != TILEXR_SUCCESS) { + std::cerr << "build spec failed: " << report.message << "\n"; + return 5; + } + if (spec.dieId != 1 || spec.missionKey != 0x059b0f03U || + spec.missionStartId != 6 || spec.missionCount != 12 || + spec.instructionStartId != 475 || spec.instructionCount != 170 || + spec.gsaStartId != 510 || spec.gsaCount != 32 || + spec.xnStartId != 1961 || spec.xnCount != 62 || + spec.ckeStartId != 332 || spec.ckeCount != 36 || + spec.channelStartId != 2 || spec.channelCount != 4) { + std::cerr << "resource spec mismatch\n"; + return 6; + } + if (report.message != "ok" || report.instructionNum != 170 || report.xnNum != 62) { + std::cerr << "report mismatch\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_rejects_invalid_basic_info_and_overflowing_windows(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_specs.h" + + #include + + using namespace TileXR; + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 0; + basic.missionKey = 0; + basic.resourceAddr = 0x100000000ULL; + basic.caps.cap0 = 15U; + basic.caps.cap1 = 15U; + basic.caps.cap2 = 15U; + basic.caps.cap3 = 15U; + + TileXRCcuSpecInfo info; + TileXRCcuSpecsReport report; + if (TileXRCcuDecodeBasicInfo(basic, &info, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "zero mission key was accepted\n"; + return 1; + } + if (report.message.find("missing CCU mission key") == std::string::npos) { + std::cerr << "weak mission diagnostic: " << report.message << "\n"; + return 2; + } + + basic.missionKey = 0x12345678U; + basic.caps.cap0 = (31U << 16) | 15U; + if (TileXRCcuDecodeBasicInfo(basic, &info, &report) != TILEXR_SUCCESS) { + std::cerr << "decode failed after fixing key: " << report.message << "\n"; + return 3; + } + + TileXRCcuResourceSpec spec; + if (TileXRCcuBuildResourceSpec(info, 65520, 10, 20, 30, 40, &spec, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "overflowing mission window was accepted\n"; + return 4; + } + if (report.message.find("mission resource window overflows") == std::string::npos) { + std::cerr << "weak overflow diagnostic: " << report.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_specs_are_wired_and_do_not_reference_hcomm_runtime_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = SPECS_HEADER.read_text(encoding="utf-8") + source = SPECS_SOURCE.read_text(encoding="utf-8") + allocator_header = ALLOCATOR_HEADER.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_specs.h", cmake) + self.assertIn("ccu/tilexr_ccu_specs.cpp", cmake) + self.assertIn("tilexr_ccu_resource_allocator.h", header) + self.assertIn("TileXRCcuBasicInfo", header) + self.assertIn("TileXRCcuSpecInfo", header) + self.assertIn("TileXRCcuDecodeBasicInfo", header) + self.assertIn("TileXRCcuBuildResourceSpec", header) + self.assertIn("gsaStartId", header) + self.assertIn("gsaStartId", source) + self.assertIn("TileXRCcuResourceSpec", allocator_header) + + combined = header + "\n" + source + for needle in [ + "#include #include #include +#include namespace { @@ -70,6 +71,53 @@ std::string RunCommand(const std::string& command) return output; } +std::string ShellQuote(const std::string& value) +{ + std::string quoted = "'"; + for (const char ch : value) { + if (ch == '\'') { + quoted += "'\\''"; + } else { + quoted += ch; + } + } + quoted += "'"; + return quoted; +} + +std::vector SplitPathspec(const std::string& pathspec) +{ + std::vector parts; + std::istringstream input(pathspec); + std::string part; + while (input >> part) { + parts.push_back(part); + } + return parts; +} + +std::string StripTrailingDot(const std::string& path) +{ + if (path.size() >= 2U && path.substr(path.size() - 2U) == "/.") { + return path.substr(0, path.size() - 2U); + } + return path; +} + +std::string ListFilesCommand(const std::string& pathspec) +{ + const std::string root = StripTrailingDot(RepoPath(".")); + std::string command = "if git -C " + ShellQuote(root) + + " rev-parse --is-inside-work-tree >/dev/null 2>&1; then git -C " + + ShellQuote(root) + " ls-files --cached --others --exclude-standard -- " + pathspec + "; else "; + for (const auto& part : SplitPathspec(pathspec)) { + command += "find " + ShellQuote(root + "/" + part) + + " -type f 2>/dev/null | sed " + ShellQuote("s#^" + root + "/##") + "; "; + } + command += "true; fi"; + return command; +} + void CheckNoLineContains(const std::string& description, const std::string& text, const std::string& needle) { std::istringstream lines(text); @@ -82,10 +130,72 @@ void CheckNoLineContains(const std::string& description, const std::string& text } } +void CheckTrackedFilesDoNotContain( + const std::string& description, + const std::string& gitPathspec, + const std::vector& forbiddenNeedles) +{ + const auto trackedFiles = RunCommand(ListFilesCommand(gitPathspec)); + + std::istringstream paths(trackedFiles); + std::string path; + while (std::getline(paths, path)) { + if (path.empty()) { + continue; + } + const auto text = ReadFile(path); + for (const auto& needle : forbiddenNeedles) { + const auto pos = text.find(needle); + if (pos != std::string::npos) { + std::cerr << "unexpected " << description << " in " << path + << ": " << needle << " at byte " << pos << std::endl; + ++g_failures; + } + } + } +} + +bool ContainsPath(const std::vector& paths, const std::string& path) +{ + for (const auto& item : paths) { + if (item == path) { + return true; + } + } + return false; +} + +void CheckTrackedFilesContainNeedleOnly( + const std::string& description, + const std::string& gitPathspec, + const std::string& needle, + const std::vector& allowedPaths) +{ + const auto trackedFiles = RunCommand(ListFilesCommand(gitPathspec)); + + std::istringstream paths(trackedFiles); + std::string path; + while (std::getline(paths, path)) { + if (path.empty()) { + continue; + } + const auto text = ReadFile(path); + const auto pos = text.find(needle); + if (pos != std::string::npos && !ContainsPath(allowedPaths, path)) { + std::cerr << "unexpected " << description << " in " << path + << ": " << needle << " at byte " << pos << std::endl; + ++g_failures; + } + } + + for (const auto& allowedPath : allowedPaths) { + CheckContains(allowedPath, ReadFile(allowedPath), needle); + } +} + void TestOpenSourceTarballsAreNotTracked() { - const std::string command = "git -C " + RepoPath(".") + " ls-files 3rdparty/open_source"; - const auto trackedFiles = RunCommand(command); + const auto trackedFiles = RunCommand(ListFilesCommand("3rdparty/open_source")); CheckNoLineContains("tracked open-source dependency archive", trackedFiles, ".tar.gz"); CheckNoLineContains("tracked open-source dependency archive", trackedFiles, ".tar.xz"); @@ -183,14 +293,651 @@ void TestCommBuildIncludesProfilingHeaders() CheckContains(commPath, commText, "${ASCEND_HOME_PATH}/${ARCH}-linux/pkg_inc/profiling/"); } -void TestChipMapCoversObservedAscend950Variants() +void TestChipNameResolverRecognizesAscend950PrVariants() { const std::string path = "src/comm/tilexr_internal.cpp"; const auto text = ReadFile(path); - CheckContains(path, text, "{\"Ascend950PR\", ChipName::CHIP_950}"); - CheckContains(path, text, "{\"Ascend950PR_9589\", ChipName::CHIP_950}"); - CheckContains(path, text, "{\"Ascend950PR_9599\", ChipName::CHIP_950}"); + CheckContains(path, text, "ChipName ResolveChipNameFromSocVersion"); + CheckContains(path, text, "{\"Ascend950PR\", ChipName::CHIP_950PR}"); + CheckContains(path, text, "{\"Ascend950PR_9589\", ChipName::CHIP_950PR}"); + CheckContains(path, text, "{\"Ascend950PR_9599\", ChipName::CHIP_950PR}"); + CheckContains(path, text, "{\"Ascend950PR_\", ChipName::CHIP_950PR}"); + CheckContains(path, text, "{\"Ascend950DT_\", ChipName::CHIP_950}"); + CheckContains(path, text, "ResolveChipNameFromSocVersion(chipName)"); +} + +void TestCommRuntimeDoesNotUseHcommOrHcclV2() +{ + const std::string path = "src/comm/CMakeLists.txt"; + const auto text = ReadFile(path); + + CheckNotContains(path, text, "hcomm"); + CheckNotContains(path, text, "hccl"); + CheckNotContains(path, text, "libhccl_v2"); + CheckNotContains(path, text, "libhccl_fwk"); + CheckNotContains(path, text, "libmc2_client"); + + CheckTrackedFilesDoNotContain("hcomm or HCCL CCU runtime dependency", "src/comm src/include", { + "#include ", + "#include \"hccl.h\"", + "pkg_inc/hcomm", + "pkg_inc/hccl", + "include/hccl", + "${ARCH}-linux/include/hccl", + "${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclAllocComResourceByTiling", + "HcclCreateOpResCtx", + "HcclEngineCtx", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "HcclChannelAcquire", + "HcclGetChannelForCcu", + "HcclAllocAlgResourceCcu", + "HcclCcuKernel", + "HcommChannelNotify", + "HcommChannelFence", + "rtGetNotifyAddress", + "HrtCcuLaunch", + "HrtGetDevResAddress", + "HrtReleaseDevResAddress", + "HrtNotifyGetAddr", + "HrtRaCustomChannel", + "HrtCntNotify", + "CcuResBatchAllocator", + "CcuResRepository", + "CcuResReq", + "CcuDeviceManager", + "CcuDevMgrImp", + "CcuRepContext", + "CcuKernelMgr", + "CtxMgrImp", + "CcuTaskParam", + "CcuTaskArg", + "GeneTaskParam", + "GetMissionKey", + "SetMissionId", + "SetMissionKey", + "SetInstrId", + "SetCcuInstrInfo", + "LoadInstruction", + "AllocIns", + "AllocCke", + "AllocXn", + "COMM_ENGINE_CCU", + "COMM_PROTOCOL_UBC_CTP", + "RT_RES_TYPE_CCU_CKE", + "RT_RES_TYPE_CCU_XN", + "HCCL_SERVER_TYPE_CCU", + }); +} + +void TestRootCMakeHcclIncludesAreNotTileCommSurface() +{ + const std::string rootPath = "CMakeLists.txt"; + const std::string commPath = "src/comm/CMakeLists.txt"; + const auto rootText = ReadFile(rootPath); + const auto commText = ReadFile(commPath); + + for (const auto& privateInclude : { + "${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl", + "${ARCH}-linux/include/hccl", + "pkg_inc/hccl", + "include/hccl", + "hccl/", + }) { + CheckNotContains(rootPath, rootText, privateInclude); + CheckNotContains(commPath, commText, privateInclude); + } +} + +void TestCommDirectCcuInstallAttemptDoesNotSubmit() +{ + const std::string commHeaderPath = "src/comm/tilexr_comm.h"; + const std::string commSourcePath = "src/comm/tilexr_comm.cpp"; + const std::string ccuRuntimeHeaderPath = "src/comm/ccu/tilexr_ccu_direct_runtime.h"; + const std::string ccuRuntimeSourcePath = "src/comm/ccu/tilexr_ccu_direct_runtime.cpp"; + const auto commHeaderText = ReadFile(commHeaderPath); + const auto commSourceText = ReadFile(commSourcePath); + const auto ccuRuntimeHeaderText = ReadFile(ccuRuntimeHeaderPath); + const auto ccuRuntimeSourceText = ReadFile(ccuRuntimeSourcePath); + + CheckContains(commHeaderPath, commHeaderText, "PrepareDirectCcuInstallAttempt"); + CheckContains(commHeaderPath, commHeaderText, "FillDirectCcuLowerLayerPlanFromAllocation"); + CheckContains(commHeaderPath, commHeaderText, "PrepareDirectCcuLowerLayerPlanCallback"); + CheckContains(commHeaderPath, commHeaderText, "std::unique_ptr ccuDirectRuntime_"); + CheckContains(commSourcePath, commSourceText, "#include \"ccu/tilexr_ccu_repository.h\""); + CheckContains(commSourcePath, commSourceText, "int TileXRComm::InitDirectCcuRuntime"); + CheckContains(commSourcePath, commSourceText, "int TileXRComm::PrepareDirectCcuInstallAttempt"); + CheckContains(commSourcePath, commSourceText, "ccuDirectRuntime_->CreateDriverAdapter"); + CheckContains(commSourcePath, commSourceText, "TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode)"); + CheckContains(commSourcePath, commSourceText, "next.lowerLayerPlan = nullptr"); + CheckContains( + commSourcePath, + commSourceText, + "next.prepareLowerLayerPlan = &TileXRComm::PrepareDirectCcuLowerLayerPlanCallback"); + CheckContains(commSourcePath, commSourceText, "next.lowerLayerPlanUserData = this"); + CheckContains(commSourcePath, commSourceText, "TileXRCcuRunDirectInstallAttempt(next, attempt, report)"); + CheckContains(ccuRuntimeHeaderPath, ccuRuntimeHeaderText, "int CreateDriverAdapter("); + CheckContains(ccuRuntimeSourcePath, ccuRuntimeSourceText, "int TileXRCcuDirectRuntime::CreateDriverAdapter"); + CheckContains(ccuRuntimeHeaderPath, ccuRuntimeHeaderText, "TileXRCcuHccpLoader"); + for (const auto& forbiddenUdmaCcuCall : { + std::string("udmaTransport_->") + "CreateCcuDriverAdapter", + std::string("udmaTransport_->") + "QueryCcuBasicInfo", + std::string("udmaTransport_->") + "RegisterCcuResourceRmaBuffer", + std::string("udmaTransport_->") + "ExportLocalCcuRmaBuffer", + std::string("udmaTransport_->") + "ExportRemoteCcuRmaBuffers", + std::string("udmaTransport_->") + "ExportLowerLayerTransportSnapshot", + }) { + CheckNotContains(commSourcePath, commSourceText, forbiddenUdmaCcuCall); + } + + const auto initUdmaBegin = commSourceText.find("int TileXRComm::InitUDMA"); + const auto initDirectCcuBegin = commSourceText.find("int TileXRComm::InitDirectCcuRuntime"); + if (initUdmaBegin == std::string::npos || initDirectCcuBegin == std::string::npos || + initUdmaBegin >= initDirectCcuBegin) { + std::cerr << commSourcePath << ": cannot isolate InitUDMA body" << std::endl; + ++g_failures; + } else { + const auto initUdmaBody = commSourceText.substr(initUdmaBegin, initDirectCcuBegin - initUdmaBegin); + CheckNotContains(commSourcePath, initUdmaBody, "RefreshDirectCcuBasicInfo"); + CheckNotContains(commSourcePath, initUdmaBody, "ResetDirectCcuBasicInfo"); + } + + const auto registerUdmaBegin = commSourceText.find("int TileXRComm::RegisterUDMAMemory"); + const auto unregisterUdmaBegin = commSourceText.find("int TileXRComm::UnregisterUDMAMemory"); + const auto getUdmaRegistryBegin = commSourceText.find("GM_ADDR TileXRComm::GetUDMARegistryPtr"); + if (registerUdmaBegin == std::string::npos || unregisterUdmaBegin == std::string::npos || + getUdmaRegistryBegin == std::string::npos || registerUdmaBegin >= unregisterUdmaBegin || + unregisterUdmaBegin >= getUdmaRegistryBegin) { + std::cerr << commSourcePath << ": cannot isolate UDMA memory registration bodies" << std::endl; + ++g_failures; + } else { + const auto registerUdmaBody = commSourceText.substr( + registerUdmaBegin, unregisterUdmaBegin - registerUdmaBegin); + const auto unregisterUdmaBody = commSourceText.substr( + unregisterUdmaBegin, getUdmaRegistryBegin - unregisterUdmaBegin); + CheckNotContains(commSourcePath, registerUdmaBody, "ResetDirectCcuLowerLayerPlan"); + CheckNotContains(commSourcePath, unregisterUdmaBody, "ResetDirectCcuLowerLayerPlan"); + } + + for (const auto& forbidden : { + "TileXRCcuPrepareSubmitTasks", + "TileXRCcuSubmitTask", + "rtCCULaunch", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "libhcomm", + "libhccl_v2", + }) { + CheckNotContains(commHeaderPath, commHeaderText, forbidden); + CheckNotContains(commSourcePath, commSourceText, forbidden); + CheckNotContains(ccuRuntimeHeaderPath, ccuRuntimeHeaderText, forbidden); + CheckNotContains(ccuRuntimeSourcePath, ccuRuntimeSourceText, forbidden); + } +} + +void TestCcuRuntimeSubmitBoundaryUsesPublicRuntimeOnly() +{ + const std::string cmakePath = "src/comm/CMakeLists.txt"; + const std::string abiConstantsHeaderPath = "src/comm/ccu/tilexr_ccu_abi_constants.h"; + const std::string barrierHeaderPath = "src/comm/ccu/tilexr_ccu_barrier_program.h"; + const std::string barrierSourcePath = "src/comm/ccu/tilexr_ccu_barrier_program.cpp"; + const std::string directHeaderPath = "src/comm/ccu/tilexr_ccu_direct_orchestrator.h"; + const std::string directSourcePath = "src/comm/ccu/tilexr_ccu_direct_orchestrator.cpp"; + const std::string directRuntimeHeaderPath = "src/comm/ccu/tilexr_ccu_direct_runtime.h"; + const std::string directRuntimeSourcePath = "src/comm/ccu/tilexr_ccu_direct_runtime.cpp"; + const std::string driverHeaderPath = "src/comm/ccu/tilexr_ccu_driver_adapter.h"; + const std::string driverSourcePath = "src/comm/ccu/tilexr_ccu_driver_adapter.cpp"; + const std::string hccpTypesHeaderPath = "src/comm/ccu/tilexr_ccu_hccp_types.h"; + const std::string hccpLoaderHeaderPath = "src/comm/ccu/tilexr_ccu_hccp_loader.h"; + const std::string hccpLoaderSourcePath = "src/comm/ccu/tilexr_ccu_hccp_loader.cpp"; + const std::string installHeaderPath = "src/comm/ccu/tilexr_ccu_install_provider.h"; + const std::string installSourcePath = "src/comm/ccu/tilexr_ccu_install_provider.cpp"; + const std::string lowerLayerPlanHeaderPath = "src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.h"; + const std::string lowerLayerPlanSourcePath = "src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.cpp"; + const std::string packageHeaderPath = "src/comm/ccu/tilexr_ccu_launch_package.h"; + const std::string packageSourcePath = "src/comm/ccu/tilexr_ccu_launch_package.cpp"; + const std::string microcodeHeaderPath = "src/comm/ccu/tilexr_ccu_microcode.h"; + const std::string microcodeSourcePath = "src/comm/ccu/tilexr_ccu_microcode.cpp"; + const std::string planHeaderPath = "src/comm/ccu/tilexr_ccu_producer_plan.h"; + const std::string planSourcePath = "src/comm/ccu/tilexr_ccu_producer_plan.cpp"; + const std::string providerHeaderPath = "src/comm/ccu/tilexr_ccu_provider.h"; + const std::string providerSourcePath = "src/comm/ccu/tilexr_ccu_provider.cpp"; + const std::string raProviderHeaderPath = "src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h"; + const std::string raProviderSourcePath = "src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp"; + const std::string repositoryHeaderPath = "src/comm/ccu/tilexr_ccu_repository.h"; + const std::string repositorySourcePath = "src/comm/ccu/tilexr_ccu_repository.cpp"; + const std::string allocatorHeaderPath = "src/comm/ccu/tilexr_ccu_resource_allocator.h"; + const std::string allocatorSourcePath = "src/comm/ccu/tilexr_ccu_resource_allocator.cpp"; + const std::string specsHeaderPath = "src/comm/ccu/tilexr_ccu_specs.h"; + const std::string specsSourcePath = "src/comm/ccu/tilexr_ccu_specs.cpp"; + const std::string headerPath = "src/comm/ccu/tilexr_ccu_runtime.h"; + const std::string sourcePath = "src/comm/ccu/tilexr_ccu_runtime.cpp"; + const auto cmakeText = ReadFile(cmakePath); + const auto abiConstantsHeaderText = ReadFile(abiConstantsHeaderPath); + const auto barrierHeaderText = ReadFile(barrierHeaderPath); + const auto barrierSourceText = ReadFile(barrierSourcePath); + const auto directHeaderText = ReadFile(directHeaderPath); + const auto directSourceText = ReadFile(directSourcePath); + const auto directRuntimeHeaderText = ReadFile(directRuntimeHeaderPath); + const auto directRuntimeSourceText = ReadFile(directRuntimeSourcePath); + const auto driverHeaderText = ReadFile(driverHeaderPath); + const auto driverSourceText = ReadFile(driverSourcePath); + const auto hccpTypesHeaderText = ReadFile(hccpTypesHeaderPath); + const auto hccpLoaderHeaderText = ReadFile(hccpLoaderHeaderPath); + const auto hccpLoaderSourceText = ReadFile(hccpLoaderSourcePath); + const auto installHeaderText = ReadFile(installHeaderPath); + const auto installSourceText = ReadFile(installSourcePath); + const auto lowerLayerPlanHeaderText = ReadFile(lowerLayerPlanHeaderPath); + const auto lowerLayerPlanSourceText = ReadFile(lowerLayerPlanSourcePath); + const auto packageHeaderText = ReadFile(packageHeaderPath); + const auto packageSourceText = ReadFile(packageSourcePath); + const auto microcodeHeaderText = ReadFile(microcodeHeaderPath); + const auto microcodeSourceText = ReadFile(microcodeSourcePath); + const auto planHeaderText = ReadFile(planHeaderPath); + const auto planSourceText = ReadFile(planSourcePath); + const auto providerHeaderText = ReadFile(providerHeaderPath); + const auto providerSourceText = ReadFile(providerSourcePath); + const auto raProviderHeaderText = ReadFile(raProviderHeaderPath); + const auto raProviderSourceText = ReadFile(raProviderSourcePath); + const auto repositoryHeaderText = ReadFile(repositoryHeaderPath); + const auto repositorySourceText = ReadFile(repositorySourcePath); + const auto allocatorHeaderText = ReadFile(allocatorHeaderPath); + const auto allocatorSourceText = ReadFile(allocatorSourcePath); + const auto specsHeaderText = ReadFile(specsHeaderPath); + const auto specsSourceText = ReadFile(specsSourcePath); + const auto headerText = ReadFile(headerPath); + const auto sourceText = ReadFile(sourcePath); + + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_abi_constants.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_barrier_program.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_barrier_program.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_driver_adapter.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_direct_orchestrator.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_direct_orchestrator.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_direct_runtime.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_direct_runtime.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_driver_adapter.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_hccp_types.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_hccp_loader.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_hccp_loader.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_launch_package.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_launch_package.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_install_provider.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_install_provider.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_lower_layer_plan_builder.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_lower_layer_plan_builder.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_microcode.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_microcode.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_producer_plan.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_producer_plan.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_provider.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_provider.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_ra_custom_channel_provider.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_ra_custom_channel_provider.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_repository.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_repository.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_resource_allocator.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_resource_allocator.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_runtime.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_runtime.cpp"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_specs.h"); + CheckContains(cmakePath, cmakeText, "ccu/tilexr_ccu_specs.cpp"); + + CheckContains(abiConstantsHeaderPath, abiConstantsHeaderText, "TILEXR_CCU_EID_BYTES"); + CheckContains(abiConstantsHeaderPath, abiConstantsHeaderText, "TILEXR_CCU_REMOTE_CCU_VA_SHIFT"); + CheckNotContains(abiConstantsHeaderPath, abiConstantsHeaderText, "runtime/kernel.h"); + CheckNotContains(abiConstantsHeaderPath, abiConstantsHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(directHeaderPath, directHeaderText, "TileXRCcuDirectInstallOptions"); + CheckContains(directHeaderPath, directHeaderText, "TileXRCcuDirectInstallAttempt"); + CheckContains(directHeaderPath, directHeaderText, "TileXRCcuDirectInstallReport"); + CheckContains(directHeaderPath, directHeaderText, "TileXRCcuRunDirectInstallAttempt"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuDecodeBasicInfo"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuBuildResourceSpec"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuResourceAllocator"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuBuildLaunchPackage"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuBindLaunchPackageInstallScope"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuBuildInstallManifest"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuInstallHardware"); + CheckContains(directSourcePath, directSourceText, "TileXRCcuPrepareSubmitTasks"); + CheckNotContains(directHeaderPath, directHeaderText, "runtime/kernel.h"); + CheckNotContains(directHeaderPath, directHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(directRuntimeHeaderPath, directRuntimeHeaderText, "TileXRCcuDirectRuntime"); + CheckContains(directRuntimeHeaderPath, directRuntimeHeaderText, "QueryBasicInfo"); + CheckContains(directRuntimeHeaderPath, directRuntimeHeaderText, "CreateDriverAdapter"); + CheckContains(directRuntimeHeaderPath, directRuntimeHeaderText, "RegisterCcuResourceRmaBuffer"); + CheckContains(directRuntimeSourcePath, directRuntimeSourceText, "TileXRCcuRaCustomChannelProvider"); + CheckContains(directRuntimeSourcePath, directRuntimeSourceText, "loader_.Load"); + CheckContains(directRuntimeSourcePath, directRuntimeSourceText, "loader_.ResolveDevicePhyId"); + CheckContains(directRuntimeSourcePath, directRuntimeSourceText, "loader_.RaCustomChannel"); + CheckNotContains(directRuntimeHeaderPath, directRuntimeHeaderText, "udma/"); + CheckNotContains(directRuntimeSourcePath, directRuntimeSourceText, "udma/"); + + CheckContains(driverHeaderPath, driverHeaderText, "TileXRCcuDriverAdapter"); + CheckContains(driverHeaderPath, driverHeaderText, "TileXRCcuCustomChannelIn"); + CheckContains(driverHeaderPath, driverHeaderText, "TILEXR_CCU_U_OP_GET_BASIC_INFO"); + CheckContains(driverHeaderPath, driverHeaderText, "TILEXR_CCU_U_OP_GET_DIE_WORKING"); + CheckContains(driverHeaderPath, driverHeaderText, "GetBasicInfo"); + CheckContains(driverHeaderPath, driverHeaderText, "GetDieEnabled"); + CheckNotContains(driverHeaderPath, driverHeaderText, "runtime/kernel.h"); + CheckNotContains(driverHeaderPath, driverHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallRequest"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallManifest"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallManifestReport"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallRequirement"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallRequirementKind"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuBuildInstallManifest"); + CheckContains(installHeaderPath, installHeaderText, "provider"); + CheckContains(installHeaderPath, installHeaderText, "manifest"); + CheckContains(installHeaderPath, installHeaderText, "installAttemptReceiptRequired"); + CheckContains(installHeaderPath, installHeaderText, "requiredEvidenceKind"); + CheckContains(installHeaderPath, installHeaderText, "requiredEvidenceSurface"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallStepEvidence"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallProviderReport"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuBuildInstallEvidence"); + CheckContains(installHeaderPath, installHeaderText, "TileXRCcuInstallHardware"); + CheckContains(installHeaderPath, installHeaderText, "installAttemptReceiptId"); + CheckContains(installSourcePath, installSourceText, "ValidateInstallRequestScope"); + CheckContains(installSourcePath, installSourceText, "ValidateInstallManifestScope"); + CheckContains(installSourcePath, installSourceText, "ValidateInstallRequestManifest"); + CheckContains(installSourcePath, installSourceText, "ValidatePublicVerifiedStepScope"); + CheckContains(installHeaderPath, installHeaderText, "requiredInstallSurfaceCount"); + CheckContains(installHeaderPath, installHeaderText, "publicVerifiedInstallSurfaceCount"); + CheckContains(installHeaderPath, installHeaderText, "missingInstallSurfaceCount"); + CheckContains(installSourcePath, installSourceText, "FillManifestInstallSurfaceCounts"); + CheckContains(installSourcePath, installSourceText, "launch install scope is stale"); + CheckContains(installSourcePath, installSourceText, "install manifest requirement kind mismatch"); + CheckContains(installSourcePath, installSourceText, "install manifest hardware requirement mismatch"); + CheckContains(installSourcePath, installSourceText, "install manifest mission requirement mismatch"); + CheckContains(installSourcePath, installSourceText, "install manifest channel requirement mismatch"); + CheckContains(installSourcePath, installSourceText, "public verified evidence scope is incomplete"); + CheckContains(installSourcePath, installSourceText, "missing CCU repository image for install manifest"); + CheckContains(installSourcePath, installSourceText, "install generated CCU repository image"); + CheckContains(installSourcePath, installSourceText, "bind CCU channel routes for sync resources"); + CheckContains(installHeaderPath, installHeaderText, "localWaitCke"); + CheckContains(installSourcePath, installSourceText, "local wait CKE"); + CheckContains(installSourcePath, installSourceText, "remote notify CKE"); + CheckContains(installSourcePath, installSourceText, "ValidateInstallReceipt"); + CheckContains(installSourcePath, installSourceText, "install attempt did not succeed"); + CheckContains(installSourcePath, installSourceText, "install attempt receipt mismatch"); + CheckContains(installSourcePath, installSourceText, "device scope mismatch"); + CheckContains(installSourcePath, installSourceText, "rank scope mismatch"); + CheckContains(installSourcePath, installSourceText, "provider scope mismatch"); + CheckContains(installHeaderPath, installHeaderText, "tilexr_ccu_provider.h"); + CheckNotContains(installHeaderPath, installHeaderText, "runtime/kernel.h"); + CheckNotContains(installHeaderPath, installHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(lowerLayerPlanHeaderPath, lowerLayerPlanHeaderText, "TileXRCcuLowerLayerPlanSpec"); + CheckContains(lowerLayerPlanHeaderPath, lowerLayerPlanHeaderText, "TileXRCcuBuildLowerLayerInstallPlan"); + CheckContains(lowerLayerPlanHeaderPath, lowerLayerPlanHeaderText, "remoteNotifyCke"); + CheckContains(lowerLayerPlanSourcePath, lowerLayerPlanSourceText, "TileXRCcuBuildPfeCtx"); + CheckContains(lowerLayerPlanSourcePath, lowerLayerPlanSourceText, "TileXRCcuBuildLocalJettyCtx"); + CheckContains(lowerLayerPlanSourcePath, lowerLayerPlanSourceText, "TileXRCcuBuildChannelCtxV1"); + CheckContains(lowerLayerPlanSourcePath, lowerLayerPlanSourceText, "localWaitCke"); + CheckNotContains(lowerLayerPlanHeaderPath, lowerLayerPlanHeaderText, "runtime/kernel.h"); + CheckNotContains(lowerLayerPlanHeaderPath, lowerLayerPlanHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuLaunchPackage"); + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuLaunchPackageReport"); + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuBuildLaunchPackage"); + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuComputeLaunchPackageFingerprint"); + CheckContains(packageSourcePath, packageSourceText, "TileXRCcuComputeLaunchPackageFingerprint"); + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuLaunchInstallScope"); + CheckContains(packageHeaderPath, packageHeaderText, "installScope"); + CheckContains(packageHeaderPath, packageHeaderText, "TileXRCcuBindLaunchPackageInstallScope"); + CheckContains(packageSourcePath, packageSourceText, "TileXRCcuBindLaunchPackageInstallScope"); + CheckContains(packageHeaderPath, packageHeaderText, "requiresHardwareInstall"); + CheckContains(packageHeaderPath, packageHeaderText, "tilexr_ccu_repository.h"); + CheckNotContains(packageHeaderPath, packageHeaderText, "runtime/kernel.h"); + CheckNotContains(packageHeaderPath, packageHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(microcodeHeaderPath, microcodeHeaderText, "struct TileXRCcuInstr"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "struct TileXRCcuSyncXnSpec"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "struct TileXRCcuCkeSpec"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "TileXRCcuEncodeLoadSqeArgsToX"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "TileXRCcuEncodeSyncXn"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "TileXRCcuEncodeSetCke"); + CheckContains(microcodeHeaderPath, microcodeHeaderText, "TileXRCcuEncodeClearCke"); + CheckContains(microcodeSourcePath, microcodeSourceText, "0x0001U"); + CheckContains(microcodeSourcePath, microcodeSourceText, "0x0802U"); + CheckContains(microcodeSourcePath, microcodeSourceText, "0x0804U"); + CheckContains(microcodeSourcePath, microcodeSourceText, "0x100dU"); + CheckNotContains(microcodeHeaderPath, microcodeHeaderText, "runtime/kernel.h"); + CheckNotContains(microcodeHeaderPath, microcodeHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(barrierHeaderPath, barrierHeaderText, "TileXRCcuBarrierSyncSpec"); + CheckContains(barrierHeaderPath, barrierHeaderText, "TileXRCcuBarrierProgramReport"); + CheckContains(barrierHeaderPath, barrierHeaderText, "TileXRCcuBuildBarrierProgram"); + CheckContains(barrierSourcePath, barrierSourceText, "TileXRCcuEncodeSyncXn"); + CheckContains(barrierSourcePath, barrierSourceText, "TileXRCcuEncodeClearCke"); + CheckNotContains(barrierHeaderPath, barrierHeaderText, "runtime/kernel.h"); + CheckNotContains(barrierHeaderPath, barrierHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuProducerPlan"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuValidateProducerPlan"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuBuildTasks"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuBuildMicrocode"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuSyncResource"); + CheckContains(planHeaderPath, planHeaderText, "localWaitCke"); + CheckContains(planHeaderPath, planHeaderText, "localWaitMask"); + CheckContains(planHeaderPath, planHeaderText, "remoteNotifyMask"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuInstructionWindow"); + CheckContains(planHeaderPath, planHeaderText, "TileXRCcuProgram"); + CheckContains(planHeaderPath, planHeaderText, "tilexr_ccu_barrier_program.h"); + CheckContains(planSourcePath, planSourceText, "TileXRCcuBuildBarrierProgram"); + CheckContains(planSourcePath, planSourceText, "spec.localWaitCke"); + CheckNotContains(planHeaderPath, planHeaderText, "runtime/kernel.h"); + CheckNotContains(planHeaderPath, planHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(providerHeaderPath, providerHeaderText, "TileXRCcuHardwareInstallEvidence"); + CheckContains(providerHeaderPath, providerHeaderText, "TileXRCcuValidateHardwareInstall"); + CheckContains(providerHeaderPath, providerHeaderText, "TileXRCcuPrepareSubmitTasks"); + CheckContains(providerHeaderPath, providerHeaderText, "submitReady"); + CheckContains(providerHeaderPath, providerHeaderText, "packageFingerprint"); + CheckContains(providerHeaderPath, providerHeaderText, "deviceId"); + CheckContains(providerHeaderPath, providerHeaderText, "rank"); + CheckContains(providerHeaderPath, providerHeaderText, "provider"); + CheckContains(providerHeaderPath, providerHeaderText, "installAttemptReceiptId"); + CheckContains(providerSourcePath, providerSourceText, "package fingerprint mismatch"); + CheckContains(providerSourcePath, providerSourceText, "launch install scope is not bound"); + CheckContains(providerSourcePath, providerSourceText, "device scope mismatch"); + CheckContains(providerSourcePath, providerSourceText, "rank scope mismatch"); + CheckContains(providerSourcePath, providerSourceText, "provider scope mismatch"); + CheckContains(providerSourcePath, providerSourceText, "install attempt receipt is missing"); + CheckContains(providerSourcePath, providerSourceText, "install attempt receipt mismatch"); + CheckContains(providerHeaderPath, providerHeaderText, "tilexr_ccu_launch_package.h"); + CheckNotContains(providerHeaderPath, providerHeaderText, "runtime/kernel.h"); + CheckNotContains(providerHeaderPath, providerHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(raProviderHeaderPath, raProviderHeaderText, "TileXRCcuRaCustomChannelProvider"); + CheckContains(raProviderHeaderPath, raProviderHeaderText, "CreateAdapter"); + CheckContains(raProviderHeaderPath, raProviderHeaderText, "TileXRCcuRaCustomChannelFunc"); + CheckContains(raProviderSourcePath, raProviderSourceText, "TILEXR_CCU_NETWORK_OFFLINE"); + CheckNotContains(raProviderHeaderPath, raProviderHeaderText, "udma/"); + CheckNotContains(raProviderHeaderPath, raProviderHeaderText, "runtime/kernel.h"); + CheckNotContains(raProviderHeaderPath, raProviderHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(hccpTypesHeaderPath, hccpTypesHeaderText, "TileXRCcuRaInfo"); + CheckContains(hccpTypesHeaderPath, hccpTypesHeaderText, "TileXRCcuRaCustomChannelFunc"); + CheckContains(hccpLoaderHeaderPath, hccpLoaderHeaderText, "TileXRCcuHccpLoader"); + CheckContains(hccpLoaderSourcePath, hccpLoaderSourceText, "dlopen(\"libra.so\", RTLD_NOW)"); + CheckContains(hccpLoaderSourcePath, hccpLoaderSourceText, "RaCustomChannel"); + CheckContains(hccpLoaderSourcePath, hccpLoaderSourceText, "rtGetDevicePhyIdByIndex"); + + CheckContains(repositoryHeaderPath, repositoryHeaderText, "TileXRCcuRepositoryImage"); + CheckContains(repositoryHeaderPath, repositoryHeaderText, "TileXRCcuRepositoryReport"); + CheckContains(repositoryHeaderPath, repositoryHeaderText, "TileXRCcuBuildRepositoryImage"); + CheckContains(repositoryHeaderPath, repositoryHeaderText, "missionOffset"); + CheckContains(repositoryHeaderPath, repositoryHeaderText, "sqeLoadOffset"); + CheckContains(repositoryHeaderPath, repositoryHeaderText, "syncOffset"); + CheckNotContains(repositoryHeaderPath, repositoryHeaderText, "runtime/kernel.h"); + CheckNotContains(repositoryHeaderPath, repositoryHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(allocatorHeaderPath, allocatorHeaderText, "TileXRCcuResourceAllocator"); + CheckContains(allocatorHeaderPath, allocatorHeaderText, "TileXRCcuResourceSpec"); + CheckContains(allocatorHeaderPath, allocatorHeaderText, "TileXRCcuResourceRequest"); + CheckContains(allocatorSourcePath, allocatorSourceText, "TileXRCcuValidateProducerPlan"); + CheckNotContains(allocatorHeaderPath, allocatorHeaderText, "runtime/kernel.h"); + CheckNotContains(allocatorHeaderPath, allocatorHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(specsHeaderPath, specsHeaderText, "TileXRCcuBasicInfo"); + CheckContains(specsHeaderPath, specsHeaderText, "TileXRCcuDecodeBasicInfo"); + CheckContains(specsHeaderPath, specsHeaderText, "TileXRCcuBuildResourceSpec"); + CheckContains(specsSourcePath, specsSourceText, "TILEXR_CCU_V1_XN_RESOURCE_OFFSET"); + CheckNotContains(specsHeaderPath, specsHeaderText, "runtime/kernel.h"); + CheckNotContains(specsHeaderPath, specsHeaderText, "rtCcuTaskInfo_t"); + + CheckContains(headerPath, headerText, "TILEXR_CCU_SQE_ARGS_LEN"); + CheckContains(headerPath, headerText, "struct TileXRCcuTask"); + CheckContains(headerPath, headerText, "TileXRCcuValidateTask"); + CheckContains(headerPath, headerText, "TileXRCcuSubmitTask"); + CheckNotContains(headerPath, headerText, "runtime/kernel.h"); + CheckNotContains(headerPath, headerText, "rtCcuTaskInfo_t"); + + CheckContains(sourcePath, sourceText, "#include "); + CheckContains(sourcePath, sourceText, "rtCcuTaskInfo_t runtimeTask"); + CheckContains(sourcePath, sourceText, "rtCCULaunch(&runtimeTask, stream)"); + CheckContains(sourcePath, sourceText, "RT_CCU_INST_CNT_INVALID"); + CheckContains(sourcePath, sourceText, "RT_CCU_INST_START_MAX"); + CheckContains(sourcePath, sourceText, "task.argSize != 1 && task.argSize != TILEXR_CCU_SQE_ARGS_LEN"); + CheckContains(sourcePath, sourceText, "TILEXR_ERROR_MKIRT"); + + for (const auto& privateNeedle : { + "#include ", + "#include \"hccl.h\"", + "pkg_inc/hcomm", + "pkg_inc/hccl", + "include/hccl", + "${ARCH}-linux/include/hccl", + "${ASCEND_HOME_PATH}/${ARCH}-linux/include/hccl", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclCcuKernel", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "HcclChannelAcquire", + "HcclGetChannelForCcu", + "CcuResBatchAllocator", + "CcuResRepository", + "GetMissionKey", + "SetMissionId", + "SetMissionKey", + "SetInstrId", + "SetCcuInstrInfo", + "LoadInstruction", + "AllocIns", + "AllocXn", + "AllocCke", + "RT_RES_TYPE_CCU_CKE", + "RT_RES_TYPE_CCU_XN", + "dlopen", + "dlsym", + }) { + CheckNotContains(directSourcePath, directSourceText, privateNeedle); + CheckNotContains(abiConstantsHeaderPath, abiConstantsHeaderText, privateNeedle); + CheckNotContains(directHeaderPath, directHeaderText, privateNeedle); + CheckNotContains(directRuntimeSourcePath, directRuntimeSourceText, privateNeedle); + CheckNotContains(directRuntimeHeaderPath, directRuntimeHeaderText, privateNeedle); + CheckNotContains(driverSourcePath, driverSourceText, privateNeedle); + CheckNotContains(driverHeaderPath, driverHeaderText, privateNeedle); + CheckNotContains(barrierSourcePath, barrierSourceText, privateNeedle); + CheckNotContains(barrierHeaderPath, barrierHeaderText, privateNeedle); + CheckNotContains(installSourcePath, installSourceText, privateNeedle); + CheckNotContains(installHeaderPath, installHeaderText, privateNeedle); + CheckNotContains(packageSourcePath, packageSourceText, privateNeedle); + CheckNotContains(packageHeaderPath, packageHeaderText, privateNeedle); + CheckNotContains(microcodeSourcePath, microcodeSourceText, privateNeedle); + CheckNotContains(microcodeHeaderPath, microcodeHeaderText, privateNeedle); + CheckNotContains(planSourcePath, planSourceText, privateNeedle); + CheckNotContains(planHeaderPath, planHeaderText, privateNeedle); + CheckNotContains(providerSourcePath, providerSourceText, privateNeedle); + CheckNotContains(providerHeaderPath, providerHeaderText, privateNeedle); + CheckNotContains(raProviderSourcePath, raProviderSourceText, privateNeedle); + CheckNotContains(raProviderHeaderPath, raProviderHeaderText, privateNeedle); + CheckNotContains(repositorySourcePath, repositorySourceText, privateNeedle); + CheckNotContains(repositoryHeaderPath, repositoryHeaderText, privateNeedle); + CheckNotContains(allocatorSourcePath, allocatorSourceText, privateNeedle); + CheckNotContains(allocatorHeaderPath, allocatorHeaderText, privateNeedle); + CheckNotContains(specsSourcePath, specsSourceText, privateNeedle); + CheckNotContains(specsHeaderPath, specsHeaderText, privateNeedle); + CheckNotContains(sourcePath, sourceText, privateNeedle); + CheckNotContains(headerPath, headerText, privateNeedle); + } + + for (const auto& loaderPrivateNeedle : { + "#include ", + "#include \"hccl.h\"", + "pkg_inc/hcomm", + "pkg_inc/hccl", + "include/hccl", + "libhcomm", + "libhccl_v2", + "libhccl_fwk", + "libmc2_client", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + }) { + CheckNotContains(hccpTypesHeaderPath, hccpTypesHeaderText, loaderPrivateNeedle); + CheckNotContains(hccpLoaderHeaderPath, hccpLoaderHeaderText, loaderPrivateNeedle); + CheckNotContains(hccpLoaderSourcePath, hccpLoaderSourceText, loaderPrivateNeedle); + } + + CheckTrackedFilesContainNeedleOnly( + "public CCU runtime launch ABI", + "src/comm src/include", + "rtCCULaunch", + {sourcePath}); + CheckTrackedFilesContainNeedleOnly( + "public CCU runtime kernel header", + "src/comm src/include", + "runtime/kernel.h", + {sourcePath}); +} + +void TestDirectCcuHeadersOwnStandardDependencies() +{ + const std::string commHeaderPath = "src/comm/tilexr_comm.h"; + const std::string abiConstantsPath = "src/comm/ccu/tilexr_ccu_abi_constants.h"; + const std::string hccpTypesPath = "src/comm/ccu/tilexr_ccu_hccp_types.h"; + const auto commHeaderText = ReadFile(commHeaderPath); + const auto abiConstantsText = ReadFile(abiConstantsPath); + const auto hccpTypesText = ReadFile(hccpTypesPath); + + CheckContains(commHeaderPath, commHeaderText, "#include "); + CheckContains(abiConstantsPath, abiConstantsText, "#include "); + CheckContains(hccpTypesPath, hccpTypesText, "#include "); + CheckContains(hccpTypesPath, hccpTypesText, "tilexr_ccu_abi_constants.h"); + CheckContains(hccpTypesPath, hccpTypesText, "TileXRCcuEndpointRouteProviderFunc"); + CheckNotContains(hccpTypesPath, hccpTypesText, "tilexr_ccu_driver_adapter.h"); + CheckNotContains(hccpTypesPath, hccpTypesText, "tilexr_ccu_lower_layer_payloads.h"); + CheckNotContains(abiConstantsPath, abiConstantsText, "udma/"); + CheckNotContains(hccpTypesPath, hccpTypesText, "udma/"); + CheckNotContains(hccpTypesPath, hccpTypesText, ""); + CheckNotContains(hccpTypesPath, hccpTypesText, "dlopen"); + CheckNotContains(hccpTypesPath, hccpTypesText, "dlsym"); + CheckNotContains(hccpTypesPath, hccpTypesText, "std::string"); } } // namespace @@ -205,7 +952,12 @@ int main() TestRuntimeEnvDoesNotPrependCannDevlib(); TestRootCMakeRespectsAscendDriverOverride(); TestCommBuildIncludesProfilingHeaders(); - TestChipMapCoversObservedAscend950Variants(); + TestChipNameResolverRecognizesAscend950PrVariants(); + TestCommRuntimeDoesNotUseHcommOrHcclV2(); + TestRootCMakeHcclIncludesAreNotTileCommSurface(); + TestCommDirectCcuInstallAttemptDoesNotSubmit(); + TestCcuRuntimeSubmitBoundaryUsesPublicRuntimeOnly(); + TestDirectCcuHeadersOwnStandardDependencies(); if (g_failures != 0) { std::cerr << g_failures << " TileXR source guard checks failed" << std::endl;