diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f80a96c..1c300f5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,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 00000000..b38f699f --- /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/docs/CCU_DIRECT_RUNTIME.md b/docs/CCU_DIRECT_RUNTIME.md new file mode 100644 index 00000000..910c0609 --- /dev/null +++ b/docs/CCU_DIRECT_RUNTIME.md @@ -0,0 +1,504 @@ +# TileXR Direct CCU 技术说明 + +本文记录 `direct-ccu-rebased` 分支中 TileXR 自研 Direct CCU 路径的技术原理、运行流程、执行方法和测试方法。它面向继续开发和问题定位,不作为外部稳定 API 文档。 + +## 1. 当前边界 + +Direct CCU 当前属于 `src/comm/ccu` 下的内部后端能力,由 `TileXRComm` 按需持有 `TileXRCcuBackend`。它的目标是在不链接 hcomm/HCCL 私有 CCU producer 的前提下,由 TileXR host 侧完成 CCU repository、mission、lower-layer 资源和 submit task 的准备,并最终通过 CANN runtime 的 `rtCCULaunch` 下发任务。 + +当前需要明确的边界如下: + +- 生产代码不得链接或包含 hcomm/HCCL 私有 CCU producer 接口。 +- 对外安装头文件当前不暴露 `TileXRDirectCcu*`、`PrepareDirectCcu`、`SubmitPrepared` 等 Direct CCU C API。 +- `TileXRCcuCollectivePlanner::Supports()` 目前仍返回 `false`,通用 collective/alltoall 尚未接入为正式 TileXR collective 后端。 +- 已维护的数据面验证路径是 direct CCU P2P copy probe,基于 CCU memory-copy microcode,不走 alltoall。 +- 默认非 P2P barrier/smoke 只能作为安装和提交链路诊断,不能替代 P2P copy 的数据正确性证明。 + +## 2. 总体架构 + +核心对象关系: + +```text +TileXRComm + | + +-- TileXRCcuBackend + | + +-- TileXRCcuRuntimeSession + | | + | +-- TileXRCcuDirectRuntime + | + +-- TileXRCcuCollectivePlanner + | + +-- TileXRCcuExecutor +``` + +主要模块职责: + +- `tilexr_ccu_backend.*`:Direct CCU 后端门面,封装 runtime session、planner 和 executor。 +- `tilexr_ccu_runtime_session.*`:管理 rank、rankSize、device、socket/thread allgather 和 direct runtime 可用状态。 +- `tilexr_ccu_direct_runtime.*`:动态加载 HCCP/RA/runtime 符号,初始化底层 RA/HDC/CCU TLV,注册 resource window,导出本端和远端 transport 信息。 +- `tilexr_ccu_resource_allocator.*`:按 driver basic info 解出的资源窗口分配 mission、repository instruction、local/remote XN、GSA、CKE、channel 等资源。 +- `tilexr_ccu_collective_planner.*`:把 runtime session、资源分配、lower-layer plan、repository 安装和 prepared submit task 串起来。 +- `tilexr_ccu_direct_orchestrator.*`:执行完整 direct install attempt,包括资源规格解码、资源分配、lower-layer plan、launch package、manifest、hardware install 和 submit task 生成。 +- `tilexr_ccu_install_provider.*`:定义硬件安装需求、证据和校验,执行 repository/lower-layer/mission 等安装步骤。 +- `tilexr_ccu_repository.*`:构造 CCU instruction repository image,并负责把 repository image 安装到设备侧。 +- `tilexr_ccu_barrier_program.*`、`tilexr_ccu_memory_program.*`、`tilexr_ccu_microcode.*`:生成 CCU barrier 和 memory-copy 指令。 +- `tilexr_ccu_runtime.*`:把 `TileXRCcuTask` 映射为 runtime task,并调用 `rtCCULaunch`。 + +## 3. 初始化与生命周期 + +`TileXRComm::Init()` 和 `TileXRComm::InitThread()` 结束前会调用 `InitCcuBackendIfEnabled()`。是否启用由环境变量控制: + +```bash +export TILEXR_ENABLE_CCU_BACKEND=1 +``` + +启用后流程如下: + +1. `TileXRComm::InitCcuBackend()` 创建 `TileXRCcuBackend`。 +2. `TileXRCcuBackend::Init()` 重置 planner,并调用 `TileXRCcuRuntimeSession::Init()`。 +3. `TileXRCcuRuntimeSession::Init()` 对单 rank communicator 直接跳过 direct runtime。 +4. 多 rank 时创建 `TileXRCcuDirectRuntime`,传入 rank、rankSize、device 和 allgather 回调。 +5. `TileXRCcuDirectRuntime::Init()` 动态加载底层符号,解析逻辑 device 到物理 device,初始化 RA/HDC 和 CCU TLV。 +6. runtime 初始化成功后刷新 direct CCU basic info,缓存 die、resource address、mission/resource 范围等基础信息。 + +失败策略是降级而不是让 communicator 初始化失败: + +- runtime 初始化失败会记录 warning,并将 direct CCU 后端关闭。 +- 失败状态按 `devId` 记录,避免一个 device 初始化失败污染同进程其他 device。 +- 后续同 device 再初始化会直接跳过,并保留明确的 unavailable message。 + +`TileXRComm::Destroy()` 会关闭 CCU backend。`TileXRCcuBackend::Shutdown()` 会先重置 planner,再关闭 runtime session;runtime session 会释放 direct runtime、basic info 缓存和 allgather 轮次状态。 + +## 4. Direct Runtime 原理 + +`TileXRCcuDirectRuntime` 是 TileXR 和底层 driver/RA/HCCP/runtime 交互的边界,承担四类工作。 + +### 4.1 符号与设备初始化 + +runtime 动态解析所需符号,不在 `tile-comm` 链接期引入 hcomm/HCCL 私有依赖。初始化阶段会: + +- 加载 HCCP/RA 相关入口。 +- 选择 direct CCU HDC 类型。 +- 解析 `logicDevId -> devicePhyId`。 +- 初始化 RA/HDC。 +- 初始化 CCU TLV。 +- 创建 driver adapter,用于 basic info 查询和后续 install。 + +如果任一关键步骤失败,runtime 返回不可用状态,并由 session 记录 device-scoped failure。 + +### 4.2 Resource Window 注册 + +CCU lower-layer/repository 安装需要一块可被 peer 识别的 resource window。当前推荐使用 RA ctx 模式: + +```bash +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE=ra_ctx +``` + +RA ctx 模式的关键步骤: + +1. 通过 `RaGetDevEidInfoNum` / `RaGetDevEidInfoList` 获取可用 EID。 +2. 通过 `TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX` 或 rank 级变量选择 EID。 +3. 调用 `RaCtxInit` 创建 RA ctx。 +4. 通过 `RaCtxTokenIdAlloc` 分配 token id。 +5. 对 CCU resource address 做页对齐后调用 `RaCtxLmemRegister`。 +6. 导出 `addr/bytes/tokenId/rawTokenId/tokenValue/eid/raCtxHandle`。 + +常用 EID 选择变量: + +```bash +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX=3 +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_RANK0=3 +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_RANK1=3 +``` + +### 4.3 Peer 信息交换 + +runtime session 为 direct runtime 提供 allgather: + +- 有 socket exchange 时使用 `TileXRSockExchange::AllGather()`。 +- thread mode 或测试模式下没有 socket exchange 时,使用进程内 thread allgather。 + +allgather 用于交换: + +- 本端 resource window token 和 EID。 +- endpoint route 信息。 +- remote XN/CKE/channel 绑定所需的 peer 资源窗口。 +- P2P copy 端点中的 source/destination address 和 process token。 + +thread allgather 带超时和失败中止逻辑,避免单 rank runtime init 失败后其他 rank 永久等待。 + +### 4.4 Endpoint Route 与 Remote Buffer + +`ExportRemoteCcuRmaBuffers()` 会把本端 resource window 信息 allgather 到所有 rank,并为每个 peer 生成 remote CCU buffer 信息。可用 RA ctx endpoint route 时,会进一步: + +- 查询本端到 peer EID 的 TP handle。 +- 交换 TP handle。 +- 导入 peer QP。 +- 使用导入得到的 TPN 和本端 doorbell token 构造 channel route。 + +这里存在一个重要细节:channel 中使用的 remote EID 采用 hcomm 兼容语义,导入后会使用反向 EID 表示。 + +## 5. 资源模型与所有权 + +Direct CCU 执行前需要把硬件资源划分为明确的窗口。资源来自 `TileXRCcuBasicInfo`,再由 `TileXRCcuDecodeBasicInfo()` 和 `TileXRCcuBuildResourceSpec()` 转换为 `TileXRCcuResourceSpec`。 + +主要资源: + +- `mission`:mission id 和 mission key。 +- `repository`:CCU instruction repository 中的指令槽。 +- `localXn`:本端 CCU XN 资源。 +- `localGsa`:本端 GSA 资源,P2P memory copy 用于装载地址、token 和长度。 +- `remoteXn`:绑定 peer 侧 XN 的本地表示。 +- `notifyCke`、`localWaitCke`、`remoteNotifyCke`:同步/完成通知使用的 CKE。 +- `channels`:指向 peer resource window/endpoint route 的 channel 绑定。 + +`TileXRCcuResourceAllocator` 使用 receipt 记录每次分配。当前释放策略是严格 LIFO,用来保证 cursor 回退不会和仍在使用的资源重叠。这个设计的目的不是做复杂资源池,而是在多次 prepare/launch 场景中先保证资源窗口所有权清晰、可诊断、可回收。 + +## 6. Lower-layer Install 原理 + +lower-layer install 负责把 CCU 执行需要的资源上下文安装到 driver 可识别的位置。主要输入来自: + +- basic info 解码出的资源窗口。 +- allocator 分配出的 mission/XN/CKE/channel range。 +- 本端 resource window token。 +- peer resource window token。 +- verified endpoint route。 + +准备流程: + +1. `PrepareDirectCcuLowerLayerTemplateFromAllocation()` 先注册本端 resource window。 +2. 导出本端 token 后,如果已经有 verified endpoint route 就配置到 runtime;否则尝试自动采集。 +3. `ExportRemoteCcuRmaBuffers()` 交换 peer token 和 endpoint 信息。 +4. `ExchangeDirectCcuRemoteNotifyCke()` 交换 peer 本地 XN/CKE/channel 分配结果。 +5. `TileXRCcuBuildLowerLayerTransportTemplate()` 构造 transport snapshot。 +6. `TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot()` 生成安装计划。 + +安装计划覆盖的 surface 包括 local XN、remote XN binding、notify CKE、channel binding、repository 和 mission/key。`tilexr_ccu_install_provider` 会为这些 surface 建立 manifest 和 install evidence。只有 install evidence 与 launch package fingerprint、device、rank、provider 和资源范围匹配时,后续 prepared submit task 才被认为可提交。 + +## 7. Repository、Mission 与 Launch Package + +Direct CCU 的 host 下发不是直接下发一段普通 kernel,而是下发 CCU mission 和 instruction repository。 + +完整 install attempt 的关键步骤在 `RunDirectInstallAttemptImpl()` 中: + +1. 校验 basic info、provider 和 repository install 输入。 +2. 解码 basic info,生成 resource spec。 +3. 分配 mission、repository、XN、GSA、CKE、channel 等资源。 +4. 如是 P2P memory copy,调整 local XN/GSA/remote XN 资源布局。 +5. 准备 lower-layer install plan。 +6. 将 producer plan 与 lower-layer proof 对齐,确保 sync resource 使用真实 peer 资源。 +7. 构造 CCU program 和 repository image。 +8. 绑定 launch package 的 device/rank/provider scope。 +9. 构造 install manifest。 +10. 执行 `TileXRCcuInstallHardware()`,安装 repository/lower-layer/mission。 +11. 根据 install evidence 生成 `submitTasks`。 + +`TileXRCcuTask` 是最终提交给 runtime 的任务描述,关键字段包括: + +- `dieId` +- `missionId` +- `key` +- `instStartId` +- `instCnt` +- `timeout` +- `argSize` +- `args[]` + +提交时 `TileXRCcuSubmitPreparedTasks()` 会逐个任务调用 `TileXRCcuSubmitTaskWithReport()`,后者在 `tilexr_ccu_runtime.cpp` 中映射到 `rtCCULaunch()`。如果中途某个 task 失败,submit report 会记录已提交数量和失败 task 的 mission/key/instruction/args 诊断信息。 + +## 8. P2P CCU Copy 执行原理 + +当前硬件数据面验证使用 direct CCU P2P copy,不走 alltoall。它验证的是 CCU 指令真实从 peer device memory 读写数据,而不是只验证 host marker 或 IPC 同步。 + +### 8.1 端点准备 + +每个 rank 分配两块 device buffer: + +- `source`:写入 rank 相关的数据 pattern。 +- `destination`:初始化为固定填充值。 + +然后通过 `rtUbDevQueryInfo(QUERY_PROCESS_TOKEN, ...)` 查询 source/destination 的 process token,打包为 CCU memory token。所有 rank 通过 session allgather 交换端点: + +```text +rank -> {sourceAddr, sourceToken, destinationAddr, destinationToken, bytes} +``` + +### 8.2 方向语义 + +`TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION` 支持两种方向: + +- `remote_to_local`:active rank 从 peer source 拷贝到本 rank destination,active rank 校验本地 destination。 +- `local_to_remote`:active rank 从本 rank source 拷贝到 peer destination,inactive rank 等待 done gate 后校验本地 destination。 + +只有 active rank 真正 submit direct CCU task;inactive rank 会打印 `p2pCcuCopy skipped`,但仍通过 done gate 参与同步,必要时校验自己的 destination。 + +### 8.3 Memory-copy Microcode + +P2P copy 会构造一个 memory copy program,典型使用 7 条 CCU 指令: + +- 把 local/remote address、token、length 装入 GSA/XN。 +- 根据方向生成 `TransRmtMemToLocMem` 或 `TransLocMemToRmtMem`。 +- 使用 CKE 完成通知,host 侧随后 `aclrtSynchronizeStream()` 等待 stream 完成。 + +通过 `TILEXR_CCU_DIRECT_TRACE=1` 可在日志中看到解码后的 CCU 指令,例如: + +```text +decoded=TransRmtMemToLocMem +decoded=TransLocMemToRmtMem +``` + +## 9. 执行方法 + +### 9.1 构建 + +在 NPU 服务器上执行: + +```bash +cd /home/tileXR +source scripts/common_env.sh +cmake --build build_ccu_direct --target tile-comm -j4 +``` + +如果 build 目录不同,需要同步修改 `TILEXR_TILE_COMM_LIB`。 + +### 9.2 两卡 P2P Copy Smoke + +以下命令是当前推荐的 direct CCU 数据面 smoke。设备号按实际健康设备调整。 + +```bash +cd /home/tileXR +source scripts/common_env.sh + +export TILEXR_TILE_COMM_LIB=/home/tileXR/build_ccu_direct/src/comm/libtile-comm.so +export TILEXR_CCU_SMOKE_DEVICES=3,2 +export TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY=1 +export TILEXR_CCU_DIRECT_SMOKE_EXPECT_P2P_CCU_COPY=1 +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_BYTES=64 +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK=0 +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_RANK0=3 +export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_RANK1=3 +export TILEXR_CCU_DIRECT_SMOKE_READY_TIMEOUT_MS=180000 +export TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 +export TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_ON_PREPARE_FAILURE=0 +export TILEXR_CCU_SMOKE_TIMEOUT=180 +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION=remote_to_local + +timeout 420s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +反方向验证: + +```bash +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION=local_to_remote +timeout 420s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +预期关键日志: + +```text +tilexr_ccu_direct_smoke prepare ret=0 ... installSucceeded=1 ... submitReady=1 +tilexr_ccu_direct_smoke submit ret=0 ... submitted=1 +tilexr_ccu_direct_smoke p2pCcuCopy ... mismatches=0 ... passed=1 +tilexr_ccu_direct_smoke_runner success +``` + +### 9.3 覆盖 `TileXRComm` 自动初始化路径 + +默认 P2P smoke 可以只用 direct CCU internal init。若要覆盖 `TileXRComm::Init()` 中的 backend auto-init 路径,增加: + +```bash +export TILEXR_ENABLE_CCU_BACKEND=1 +export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=0 +timeout 420s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +### 9.4 Dry-run 参数检查 + +不触碰 ACL/NPU,只检查 runner 推导出的 repository/task 参数: + +```bash +export TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 +export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY=1 +export TILEXR_CCU_DIRECT_SMOKE_DRY_RUN=1 +bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +## 10. 测试矩阵 + +### 10.1 本地单元测试 + +推荐先跑 CCU 相关 Python 测试: + +```bash +python3 -m unittest \ + tests.ccu.test_tilexr_ccu_resource_allocator \ + tests.ccu.test_tilexr_ccu_lower_layer_plan_builder \ + tests.ccu.test_tilexr_ccu_backend_boundary \ + tests.ccu.test_tilexr_ccu_direct_orchestrator \ + tests.ccu.test_tilexr_ccu_direct_smoke_probe \ + tests.ccu.test_tilexr_ccu_direct_smoke_runner \ + tests.ccu.test_tilexr_ccu_public_comm_api +``` + +覆盖更完整的 CCU suite: + +```bash +python3 -m unittest discover tests/ccu +``` + +### 10.2 依赖边界检查 + +每次修改 `src/comm/ccu` 后应确认 `tile-comm` 没有引入 hcomm/HCCL 私有 CCU 依赖: + +```bash +source scripts/common_env.sh +bash tests/ccu/check_tile_comm_no_hcomm_deps.sh build/src/comm/libtile-comm.so +``` + +### 10.3 硬件 Smoke + +硬件 smoke 由 `tests/ccu/run_tilexr_ccu_direct_smoke.sh` 启动两个 rank 进程。runner 默认是安全的,必须设置: + +```bash +export TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 +``` + +runner 会: + +- 编译 `tests/ccu/ccu_tilexr_direct_smoke_probe.cpp`。 +- 检查 `npu-smi info` 是否可完成。 +- 默认拒绝 busy/unhealthy 设备。 +- 分别启动 rank0/rank1。 +- 为 prepare、install、submit、p2p result 做日志断言。 +- 用 `timeout` 包住 rank 进程,避免测试卡死。 + +如确认设备健康但 `npu-smi` health 字段不是 OK,可显式放开 unhealthy 检查: + +```bash +export TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU=1 +``` + +不要在没有明确授权时设置: + +```bash +export TILEXR_CCU_SMOKE_ALLOW_BUSY_NPU=1 +``` + +### 10.4 前序验证记录 + +在 950 验证服务器的前序复测中,当前分支曾通过以下检查: + +- `cmake --build build_ccu_direct --target tile-comm -j4` +- 143 个 CCU 单元测试通过,1 个 skip。 +- 两卡 P2P copy smoke 在 `remote_to_local` 和 `local_to_remote` 两个方向通过。 +- `TILEXR_ENABLE_CCU_BACKEND=1` 且 `TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT=0` 的自动初始化路径通过 P2P copy smoke。 + +注意:默认非 P2P smoke 曾暴露 repository install 旧参数问题,因此不能把默认非 P2P smoke 作为当前推荐验证路径。 + +## 11. 常见问题定位 + +### 11.1 runtime 初始化失败 + +看日志中的: + +```text +TileXR direct CCU runtime init failed +logicDevId +devicePhyId +hdcType +raInitialized +ccuTlvInitialized +message +``` + +如果同一 device 后续被跳过,说明 device-scoped unavailable 状态已经记录。需要先解决首次失败原因,或换健康 device 重试。 + +### 11.2 prepare 卡住或 allgather 超时 + +重点检查: + +- rank0/rank1 是否都启动。 +- `TILEXR_COMM_ID`、端口和 rankSize 是否一致。 +- 某个 rank 是否先因 runtime unavailable 退出。 +- thread mode 下 uid 是否一致。 +- `TILEXR_CCU_DIRECT_SMOKE_READY_TIMEOUT_MS` 是否过短。 + +### 11.3 resource window 注册失败 + +重点检查: + +- 是否设置 `TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE=ra_ctx`。 +- EID index 是否在 `RaGetDevEidInfoList` 返回列表内。 +- 是否选择了错误 device 或 busy device。 +- resource address 是否来自当前 device 的 CCU basic info。 + +可打开: + +```bash +export TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE=1 +``` + +查看 EID、TPN、doorbell token、QP import 等信息。 + +### 11.4 repository 或 lower-layer install 失败 + +重点看 prepare report: + +- `installAttempted` +- `installSucceeded` +- `requiredInstallSurfaceCount` +- `publicVerifiedInstallSurfaceCount` +- `missingInstallSurfaceCount` +- `message` + +如果缺失 surface,通常说明 lower-layer plan 没有拿到完整 peer route/token,或 resource range 与 manifest 不匹配。 + +### 11.5 submit 失败 + +submit 失败时 report 会打印 task 关键字段: + +```text +missionId +key +instStartId +instCnt +argSize +args[] +rtRet +``` + +定位顺序: + +1. 确认 prepare 阶段 `submitReady=1`。 +2. 确认 repository mission window 覆盖 task 的 `instStartId/instCnt`。 +3. 确认 mission id/key 与 install manifest 匹配。 +4. 确认 stream 非空。 +5. 打开 `TILEXR_CCU_DIRECT_TRACE=1` 查看最终 task 和 microcode 解码。 + +### 11.6 P2P copy 不匹配 + +重点区分方向: + +- `remote_to_local`:active rank 校验本地 destination。 +- `local_to_remote`:inactive rank 等待 active rank done 后校验本地 destination。 + +常见原因: + +- active rank 设置错误。 +- direction 与期望校验 rank 不一致。 +- process token 查询失败。 +- endpoint route 不完整导致 channel 指向错误 peer。 +- submit 成功但 stream synchronize 失败。 + +## 12. 后续扩展建议 + +当前代码已经具备 direct runtime、resource allocator、lower-layer install、repository install、mission/task submit 和 P2P copy 数据面验证基础。后续要做泛化 alltoall/collective,建议按以下顺序推进: + +1. 先把 collective request 到 producer plan 的映射补齐,而不是绕过 `TileXRCcuCollectivePlanner::Supports()`。 +2. 复用现有 resource allocator 和 lower-layer evidence,不新增无证据的 env override。 +3. 用多 task submit 的单元测试覆盖 launch package 批量下发。 +4. 在两卡 P2P copy 稳定后,再扩展到 4 卡 N-to-N 数据流。 +5. alltoall 数据正确性必须以 device buffer 内容校验为准,不能用 host marker 或单纯 submit 成功替代。 + diff --git a/docs/superpowers/plans/2026-07-07-ccu-api-isolation.md b/docs/superpowers/plans/2026-07-07-ccu-api-isolation.md new file mode 100644 index 00000000..bf5820ce --- /dev/null +++ b/docs/superpowers/plans/2026-07-07-ccu-api-isolation.md @@ -0,0 +1,1631 @@ +# CCU API Isolation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Refactor direct CCU from a public task API into an internal collective backend selected through `AUTO`, `AIV`, `UDMA`, or `CCU` collective options. + +**Architecture:** Keep `tilexr_api.h` free of CCU symbols. Add high-level backend selection to `tilexr_collectives.h`, route collective calls through a small dispatcher, and make CCU an internal `TileXRCcuBackend` owned opaquely by `TileXRComm`. Inside CCU, separate runtime/session concerns from planning and task execution. + +**Tech Stack:** C++14, C ABI-compatible exported functions, CMake, existing TileXR collectives tests, Python source-guard tests, Ascend runtime stubs where already used by this branch. + +## Global Constraints + +- Do not install or introduce a public `tilexr_ccu_api.h`. +- `src/include/tilexr_api.h` must not contain `CCU`, `Ccu`, `DirectCcu`, or `TILEXR_DIRECT_CCU`. +- Public headers must not expose `TileXRDirectCcu*`, `PrepareDirectCcu`, `SubmitPrepared`, `Repository`, `SQE`, `XN`, `CKE`, or CCU task descriptors. +- `tilexr_collectives.h` may expose high-level backend enum values for `AUTO`, `AIV`, `UDMA`, and `CCU`. +- Null or zero-initialized collective options select `AUTO`. +- Forced `UDMA` and forced `CCU` must not silently fall back when unavailable or unsupported. +- Do not add a generic backend manager in this pass; `TileXRComm` owns only the CCU backend context. +- Preserve existing AIV collective behavior for old entry points by making old entry points call the new `*Ex` path with `AUTO`. + +--- + +## File Structure + +- `src/include/tilexr_collectives.h`: public high-level backend enum, collective options, and `*Ex` collective APIs. +- `src/collectives/host/tilexr_collectives.cpp`: shared validation, `*Ex` implementations, old API forwarding, and backend dispatch. +- `src/collectives/host/collective_backend.h`: internal backend-selection helpers for `AUTO`, `AIV`, `UDMA`, and `CCU`. +- `src/collectives/host/collective_backend.cpp`: dispatch implementation and fake-state hooks for focused unit tests. +- `src/comm/tilexr_comm.h`: remove direct CCU includes/state; forward-declare `TileXRCcuBackend`; expose narrow internal CCU backend accessors. +- `src/comm/tilexr_comm.cpp`: delegate CCU lifecycle and direct CCU helper logic to `TileXRCcuBackend`. +- `src/comm/comm_wrap.cpp`: remove direct CCU public C API bridge and all direct CCU public constants/helpers. +- `src/comm/ccu/tilexr_ccu_backend.h`: internal CCU backend facade used by `TileXRComm` and collective dispatch. +- `src/comm/ccu/tilexr_ccu_backend.cpp`: backend facade implementation, initially moving behavior from `TileXRComm`. +- `src/comm/ccu/tilexr_ccu_runtime_session.h/.cpp`: runtime availability, driver adapter, basic info, RA/HCCP exchange, shutdown. +- `src/comm/ccu/tilexr_ccu_collective_planner.h/.cpp`: convert typed collective requests to existing CCU install/submit plans. +- `src/comm/ccu/tilexr_ccu_executor.h/.cpp`: submit prepared CCU tasks and map runtime failures to TileXR error codes. +- `src/comm/CMakeLists.txt`: add new CCU/backend sources; keep CCU linked into `tile-comm`. +- `tests/collectives/unit/test_tilexr_collective_backend_options.cpp`: compile/runtime checks for public options and dispatch fallback semantics. +- `tests/ccu/test_tilexr_ccu_public_comm_api.py`: invert old public API tests into source guards for no public CCU task API. +- `tests/ccu/test_tilexr_ccu_backend_boundary.py`: source guards for `TileXRCcuBackend` ownership and no direct CCU fields on `TileXRComm`. +- Existing CCU tests under `tests/ccu`: update references from `TileXRComm` direct CCU methods to backend facade or `TILEXR_CCU_TESTING` hooks. + +--- + +### Task 1: Public Collective Backend Options And `*Ex` API + +**Files:** +- Modify: `src/include/tilexr_collectives.h` +- Modify: `src/collectives/host/tilexr_collectives.cpp` +- Create: `tests/collectives/unit/test_tilexr_collective_backend_options.cpp` +- Modify: `tests/collectives/CMakeLists.txt` + +**Interfaces:** +- Produces: + - `enum TileXRCollectiveBackend` + - `struct TileXRCollectiveOptions` + - `TileXRAllGatherEx`, `TileXRAllToAllEx`, `TileXRAllReduceEx`, `TileXRReduceScatterEx`, `TileXRBroadcastEx` + - `TileXRProfileProbeEx` +- Consumes: + - Existing `TileXRAllGather`, `TileXRAllToAll`, `TileXRAllReduce`, `TileXRReduceScatter`, `TileXRBroadcast`, `TileXRProfileProbe` + +- [ ] **Step 1: Write the failing public header test** + +Create `tests/collectives/unit/test_tilexr_collective_backend_options.cpp`: + +```cpp +#include "tilexr_collectives.h" + +#include + +namespace { + +static_assert(TILEXR_COLLECTIVE_BACKEND_AUTO == 0, "AUTO must be zero for zero-initialized options"); +static_assert(TILEXR_COLLECTIVE_BACKEND_AIV == 1, "AIV enum value changed"); +static_assert(TILEXR_COLLECTIVE_BACKEND_UDMA == 2, "UDMA enum value changed"); +static_assert(TILEXR_COLLECTIVE_BACKEND_CCU == 3, "CCU enum value changed"); + +int CheckFunctionPointers() +{ + TileXRCollectiveOptions options {}; + if (options.backend != TILEXR_COLLECTIVE_BACKEND_AUTO) { + return 1; + } + + auto allGather = &TileXRAllGatherEx; + auto allToAll = &TileXRAllToAllEx; + auto allReduce = &TileXRAllReduceEx; + auto reduceScatter = &TileXRReduceScatterEx; + auto broadcast = &TileXRBroadcastEx; + auto profileProbe = &TileXRProfileProbeEx; + + (void)allGather; + (void)allToAll; + (void)allReduce; + (void)reduceScatter; + (void)broadcast; + (void)profileProbe; + return 0; +} + +} // namespace + +int main() +{ + return CheckFunctionPointers(); +} +``` + +- [ ] **Step 2: Register the failing test target** + +In `tests/collectives/CMakeLists.txt`, add near the other unit executables: + +```cmake +add_executable(test_tilexr_collective_backend_options + unit/test_tilexr_collective_backend_options.cpp +) +``` + +After the existing `target_link_libraries(test_tilexr_collectives_header_compile ...)` block, add: + +```cmake +target_link_libraries(test_tilexr_collective_backend_options PRIVATE ${TILEXR_COLLECTIVES_TEST_TARGET}) +``` + +Add `test_tilexr_collective_backend_options` to the `foreach(_tilexr_collectives_link_target ...)` list immediately +after `test_tilexr_collectives_header_compile`. + +Add this test registration immediately after `add_test(NAME test_tilexr_collectives_header_compile ...)`: + +```cmake +add_test(NAME test_tilexr_collective_backend_options COMMAND test_tilexr_collective_backend_options) +``` + +Add `test_tilexr_collective_backend_options` to the `install(TARGETS ...)` list immediately after +`test_tilexr_collectives_header_compile`. + +- [ ] **Step 3: Run test to verify it fails** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options -j$(nproc) +``` + +Expected: compile failure mentioning `TILEXR_COLLECTIVE_BACKEND_AUTO` or `TileXRAllGatherEx` is not declared. + +- [ ] **Step 4: Add public enum, options, and `*Ex` declarations** + +In `src/include/tilexr_collectives.h`, add inside `extern "C"` before the function declarations: + +```cpp +enum TileXRCollectiveBackend { + TILEXR_COLLECTIVE_BACKEND_AUTO = 0, + TILEXR_COLLECTIVE_BACKEND_AIV = 1, + TILEXR_COLLECTIVE_BACKEND_UDMA = 2, + TILEXR_COLLECTIVE_BACKEND_CCU = 3, +}; + +struct TileXRCollectiveOptions { + TileXRCollectiveBackend backend; +}; +``` + +Then declare the `*Ex` variants: + +```cpp +int TileXRAllGatherEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); +int TileXRAllToAllEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); +int TileXRAllReduceEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options); +int TileXRReduceScatterEx(void *sendBuf, void *recvBuf, int64_t recvCount, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, + const TileXRCollectiveOptions *options); +int TileXRBroadcastEx(void *buf, int64_t count, + TileXR::TileXRDataType dataType, int root, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options); +int TileXRProfileProbeEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); +``` + +- [ ] **Step 5: Add minimal `*Ex` implementations and old API forwarding** + +In `src/collectives/host/tilexr_collectives.cpp`, add helper: + +```cpp +TileXRCollectiveBackend SelectedBackend(const TileXRCollectiveOptions *options) +{ + return options == nullptr ? TILEXR_COLLECTIVE_BACKEND_AUTO : options->backend; +} +``` + +Rename each existing function body to the corresponding `*Ex` function and accept `const TileXRCollectiveOptions *options`. At the top of each `*Ex` after validation, call: + +```cpp +const TileXRCollectiveBackend backend = SelectedBackend(options); +(void)backend; +``` + +Then make the old function forward to `nullptr` options: + +```cpp +int TileXRAllGather(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream) +{ + return TileXRAllGatherEx(sendBuf, recvBuf, sendCount, dataType, comm, stream, nullptr); +} +``` + +Repeat the same forwarding pattern for all existing collective entry points. + +- [ ] **Step 6: Run test to verify it passes** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options -j$(nproc) +ctest --test-dir build -R test_tilexr_collective_backend_options --output-on-failure +``` + +Expected: target builds and the test passes. + +- [ ] **Step 7: Commit** + +```bash +git add src/include/tilexr_collectives.h src/collectives/host/tilexr_collectives.cpp tests/collectives/CMakeLists.txt tests/collectives/unit/test_tilexr_collective_backend_options.cpp +git commit -m "feat: add collective backend options" +``` + +--- + +### Task 2: Backend Dispatch Semantics For `AUTO`, `AIV`, `UDMA`, And `CCU` + +**Files:** +- Create: `src/collectives/host/collective_backend.h` +- Create: `src/collectives/host/collective_backend.cpp` +- Modify: `src/collectives/host/tilexr_collectives.cpp` +- Modify: `src/collectives/CMakeLists.txt` +- Modify: `tests/collectives/CMakeLists.txt` +- Modify: `tests/collectives/unit/test_tilexr_collective_backend_options.cpp` + +**Interfaces:** +- Consumes: + - `TileXRCollectiveBackend` + - `TileXRCollectiveOptions` + - Existing AIV launch path in `tilexr_collectives.cpp` +- Produces: + - `TileXRCollectives::Host::CollectiveRequest` + - `TileXRCollectives::Host::DispatchCollective` + - `TileXRCollectives::Host::SetBackendTestState` + - `TileXRCollectives::Host::ResetBackendTestState` + +- [ ] **Step 1: Extend test with fake backend-state cases** + +Add this include after `#include "tilexr_collectives.h"`: + +```cpp +#include "collective_backend.h" +``` + +Append this function before `main()`: + +```cpp + +int CheckBackendDispatch() +{ + using TileXRCollectives::Host::BackendTestState; + using TileXRCollectives::Host::CollectiveRequest; + using TileXRCollectives::Host::DispatchCollective; + using TileXRCollectives::Host::ResetBackendTestState; + using TileXRCollectives::Host::SetBackendTestState; + + CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL_GATHER; + request.sendBuf = reinterpret_cast(0x1000); + request.recvBuf = reinterpret_cast(0x2000); + request.count = 1; + request.dataType = TileXR::TILEXR_DATA_TYPE_INT32; + request.comm = reinterpret_cast(0x3000); + request.stream = nullptr; + + BackendTestState state {}; + state.aivReturn = TileXR::TILEXR_SUCCESS; + state.udmaInitialized = false; + state.ccuInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_AUTO) != TileXR::TILEXR_SUCCESS) { + return 2; + } + + state.udmaInitialized = true; + state.udmaSupported = true; + state.udmaReturn = TileXR::TILEXR_SUCCESS; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_UDMA) != TileXR::TILEXR_SUCCESS) { + return 3; + } + + state.udmaInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_UDMA) != TileXR::TILEXR_ERROR_NOT_INITIALIZED) { + return 4; + } + + state.ccuInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_ERROR_NOT_INITIALIZED) { + return 5; + } + + ResetBackendTestState(); + return 0; +} +``` + +Change `main()` to: + +```cpp +int main() +{ + const int pointerRet = CheckFunctionPointers(); + if (pointerRet != 0) { + return pointerRet; + } + return CheckBackendDispatch(); +} +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options -j$(nproc) +``` + +Expected: compile failure mentioning `collective_backend.h` is missing. + +- [ ] **Step 3: Add backend dispatch interfaces** + +Create `src/collectives/host/collective_backend.h`: + +```cpp +#ifndef TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H +#define TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H + +#include + +#include "acl/acl_base.h" +#include "tilexr_collectives.h" +#include "tilexr_types.h" + +namespace TileXRCollectives { +namespace Host { + +struct CollectiveRequest { + TileXR::TileXRType type = TileXR::TileXRType::ALL_GATHER; + void *sendBuf = nullptr; + void *recvBuf = nullptr; + int64_t count = 0; + TileXR::TileXRDataType dataType = TileXR::TILEXR_DATA_TYPE_RESERVED; + TileXR::TileXRReduceOp reduceOp = TileXR::TILEXR_REDUCE_RESERVED; + int root = 0; + TileXRCommPtr comm = nullptr; + aclrtStream stream = nullptr; +}; + +struct BackendTestState { + bool enabled = false; + bool udmaInitialized = false; + bool udmaSupported = false; + int udmaReturn = TileXR::TILEXR_ERROR_NOT_SUPPORT; + bool ccuInitialized = false; + bool ccuSupported = false; + int ccuReturn = TileXR::TILEXR_ERROR_NOT_SUPPORT; + int aivReturn = TileXR::TILEXR_SUCCESS; +}; + +int DispatchCollective(const CollectiveRequest &request, TileXRCollectiveBackend backend); +void SetBackendTestState(const BackendTestState &state); +void ResetBackendTestState(); + +} // namespace Host +} // namespace TileXRCollectives + +#endif // TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H +``` + +- [ ] **Step 4: Add minimal dispatch implementation** + +Create `src/collectives/host/collective_backend.cpp`: + +```cpp +#include "collective_backend.h" + +namespace TileXRCollectives { +namespace Host { +namespace { + +BackendTestState g_testState {}; + +int DispatchAiv(const CollectiveRequest&) +{ + return g_testState.enabled ? g_testState.aivReturn : TileXR::TILEXR_SUCCESS; +} + +int DispatchUdma(const CollectiveRequest&) +{ + if (!g_testState.enabled || !g_testState.udmaInitialized) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return g_testState.udmaSupported ? g_testState.udmaReturn : TileXR::TILEXR_ERROR_NOT_SUPPORT; +} + +int DispatchCcu(const CollectiveRequest&) +{ + if (!g_testState.enabled || !g_testState.ccuInitialized) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return g_testState.ccuSupported ? g_testState.ccuReturn : TileXR::TILEXR_ERROR_NOT_SUPPORT; +} + +} // namespace + +int DispatchCollective(const CollectiveRequest &request, TileXRCollectiveBackend backend) +{ + if (request.comm == nullptr || request.sendBuf == nullptr || request.recvBuf == nullptr || request.count <= 0) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + switch (backend) { + case TILEXR_COLLECTIVE_BACKEND_AIV: + return DispatchAiv(request); + case TILEXR_COLLECTIVE_BACKEND_UDMA: + return DispatchUdma(request); + case TILEXR_COLLECTIVE_BACKEND_CCU: + return DispatchCcu(request); + case TILEXR_COLLECTIVE_BACKEND_AUTO: + default: + if (g_testState.enabled && g_testState.ccuInitialized && g_testState.ccuSupported) { + return DispatchCcu(request); + } + if (g_testState.enabled && g_testState.udmaInitialized && g_testState.udmaSupported) { + return DispatchUdma(request); + } + return DispatchAiv(request); + } +} + +void SetBackendTestState(const BackendTestState &state) +{ + g_testState = state; + g_testState.enabled = true; +} + +void ResetBackendTestState() +{ + g_testState = BackendTestState {}; +} + +} // namespace Host +} // namespace TileXRCollectives +``` + +- [ ] **Step 5: Build/link the dispatch source** + +In `src/collectives/CMakeLists.txt`, add `host/collective_backend.cpp` and `host/collective_backend.h` to the `tilexr-collectives` source list. + +In `tests/collectives/CMakeLists.txt`, add a private include directory for the new test: + +```cmake +target_include_directories(test_tilexr_collective_backend_options PRIVATE + ${TILEXR_ROOT}/src/collectives/host +) +``` + +- [ ] **Step 6: Route `*Ex` calls through dispatch without changing AIV behavior** + +In `tilexr_collectives.cpp`, include `collective_backend.h`. + +For each `*Ex`, after existing validation and loopback handling, build a `CollectiveRequest` and call `DispatchCollective`. In this task, keep AIV launch by returning the existing launch result for `AIV` and `AUTO`; forced `UDMA`/`CCU` semantics are exercised by tests through fake dispatch state. + +Use this local helper to keep the old AIV launch path explicit: + +```cpp +bool UsesForcedNonAivBackend(TileXRCollectiveBackend backend) +{ + return backend == TILEXR_COLLECTIVE_BACKEND_UDMA || backend == TILEXR_COLLECTIVE_BACKEND_CCU; +} +``` + +In `TileXRAllGatherEx`, before the AIV launch: + +```cpp +if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL_GATHER; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = sendCount; + request.dataType = dataType; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); +} +``` + +Repeat this pattern for the other collectives: + +```cpp +// TileXRAllToAllEx +request.type = TileXR::TileXRType::ALL2ALL; +request.count = sendCount; + +// TileXRAllReduceEx +request.type = TileXR::TileXRType::ALL_REDUCE; +request.count = count; +request.reduceOp = op; + +// TileXRReduceScatterEx +request.type = TileXR::TileXRType::REDUCE_SCATTER; +request.count = recvCount; +request.reduceOp = op; + +// TileXRBroadcastEx +request.type = TileXR::TileXRType::BROADCAST; +request.sendBuf = buf; +request.recvBuf = buf; +request.count = count; +request.root = root; + +// TileXRProfileProbeEx +request.type = TileXR::TileXRType::PROFILE_PROBE; +request.count = count; +``` + +- [ ] **Step 7: Run focused tests** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options -j$(nproc) +ctest --test-dir build -R test_tilexr_collective_backend_options --output-on-failure +``` + +Expected: test passes. + +- [ ] **Step 8: Commit** + +```bash +git add src/collectives/host/collective_backend.h src/collectives/host/collective_backend.cpp src/collectives/host/tilexr_collectives.cpp src/collectives/CMakeLists.txt tests/collectives/CMakeLists.txt tests/collectives/unit/test_tilexr_collective_backend_options.cpp +git commit -m "feat: route collective backend selection" +``` + +--- + +### Task 3: Remove Direct CCU Public API From Installed Headers And `comm_wrap.cpp` + +**Files:** +- Modify: `src/include/tilexr_api.h` +- Modify: `src/comm/comm_wrap.cpp` +- Modify: `tests/ccu/test_tilexr_ccu_public_comm_api.py` +- Delete: `tests/ccu/ccu_public_direct_api_compile_probe.c` +- Modify: `tests/ccu/test_tilexr_ccu_public_api_compile_probe.py` + +**Interfaces:** +- Consumes: + - Existing generic TileXR C API declarations. +- Produces: + - A clean `tilexr_api.h`. + - No direct CCU public wrapper implementation in `comm_wrap.cpp`. + +- [ ] **Step 1: Rewrite public source-guard test** + +Replace `tests/ccu/test_tilexr_ccu_public_comm_api.py` with: + +```python +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PUBLIC_HEADERS = [ + REPO_ROOT / "src" / "include" / "tilexr_api.h", + REPO_ROOT / "src" / "include" / "tilexr_types.h", + REPO_ROOT / "src" / "include" / "tilexr_collectives.h", +] +CORE_API_HEADER = REPO_ROOT / "src" / "include" / "tilexr_api.h" +COMM_WRAP = REPO_ROOT / "src" / "comm" / "comm_wrap.cpp" + + +class TileXRCcuPublicCommApiTest(unittest.TestCase): + def test_core_api_header_has_no_ccu_symbols(self): + header = CORE_API_HEADER.read_text(encoding="utf-8") + for needle in ["CCU", "Ccu", "DirectCcu", "TILEXR_DIRECT_CCU"]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + + def test_installed_public_headers_do_not_expose_low_level_ccu_model(self): + forbidden = [ + "TileXRDirectCcu", + "PrepareDirectCcu", + "SubmitPrepared", + "Repository", + "SQE", + " XN", + " CKE", + "TaskInfo", + "rtCCULaunch", + "rtCcuTaskInfo_t", + "hcomm", + "hccl", + ] + for path in PUBLIC_HEADERS: + text = path.read_text(encoding="utf-8") + for needle in forbidden: + with self.subTest(path=path.name, needle=needle): + self.assertNotIn(needle, text) + + def test_collectives_header_only_exposes_high_level_backend_names(self): + text = (REPO_ROOT / "src" / "include" / "tilexr_collectives.h").read_text(encoding="utf-8") + for needle in [ + "TILEXR_COLLECTIVE_BACKEND_AUTO", + "TILEXR_COLLECTIVE_BACKEND_AIV", + "TILEXR_COLLECTIVE_BACKEND_UDMA", + "TILEXR_COLLECTIVE_BACKEND_CCU", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, text) + + def test_comm_wrap_has_no_direct_ccu_public_bridge(self): + wrapper = COMM_WRAP.read_text(encoding="utf-8") + for needle in [ + "TileXRCommInitRankDirectCcuWithDomain", + "TileXRCommPrepareDirectCcu", + "TileXRCommPrepareDirectCcuMemoryCopy", + "TileXRDirectCcuGetPreparedTask", + "TileXRDirectCcuSubmitPrepared", + "TileXRCommReadDirectCcuInstructions", + "TileXRDirectCcuDestroyPrepared", + "TileXRDirectCcuPreparedTasks", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, wrapper) + + +if __name__ == "__main__": + unittest.main() +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_public_comm_api.py +``` + +Expected: failures showing CCU symbols in `tilexr_api.h` and `comm_wrap.cpp`. + +- [ ] **Step 3: Remove CCU declarations from `tilexr_api.h`** + +Delete these declaration groups from `src/include/tilexr_api.h`: + +```cpp +typedef void *TileXRDirectCcuPreparedTasksPtr; +#define TILEXR_DIRECT_CCU_REPORT_MESSAGE_BYTES 2048 +... +int TileXRCommInitRankDirectCcuWithDomain(...); +... +int TileXRDirectCcuDestroyPrepared(TileXRDirectCcuPreparedTasksPtr prepared); +``` + +Keep `TileXRCommPtr`, UDMA/SDMA APIs, DFX APIs, and generic comm lifecycle APIs unchanged. + +- [ ] **Step 4: Remove direct CCU bridge from `comm_wrap.cpp`** + +Delete the anonymous-namespace direct CCU helpers and public wrapper functions: + +```cpp +TileXRDirectCcuPreparedTasks +CopyDirectCcuMessage +FillPublicPrepareReport +FillPublicSubmitReport +FillPublicInstructionReadbackReport +RepositoryInstallWindowFromPublic +RepositoryInstallDataLenModeFromPublic +RepositoryMemoryAllocModeFromPublic +InstallOrderFromPublic +MakeDirectCcuOptions +PreparedHandle +MemoryCopyDirectionFromPublic +TileXRCommInitRankDirectCcuWithDomain +TileXRCommPrepareDirectCcu +TileXRCommPrepareDirectCcuMemoryCopy +TileXRDirectCcuGetPreparedTask +TileXRDirectCcuSubmitPrepared +TileXRDirectCcuSubmitPreparedTask +TileXRCommReadDirectCcuInstructions +TileXRDirectCcuCreatePreparedForTest +TileXRDirectCcuDestroyPrepared +``` + +After removal, `comm_wrap.cpp` should include no CCU internal headers and no direct CCU constants. + +- [ ] **Step 5: Remove external direct CCU compile probe** + +Delete `tests/ccu/ccu_public_direct_api_compile_probe.c`. + +Replace `tests/ccu/test_tilexr_ccu_public_api_compile_probe.py` with a guard that asserts the deleted file is gone: + +```python +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +class TileXRCcuPublicApiCompileProbeTest(unittest.TestCase): + def test_external_direct_ccu_public_probe_removed(self): + self.assertFalse((REPO_ROOT / "tests" / "ccu" / "ccu_public_direct_api_compile_probe.c").exists()) + + +if __name__ == "__main__": + unittest.main() +``` + +- [ ] **Step 6: Run source guards** + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_public_comm_api.py +python3 tests/ccu/test_tilexr_ccu_public_api_compile_probe.py +``` + +Expected: both pass. + +- [ ] **Step 7: Commit** + +```bash +git add src/include/tilexr_api.h src/comm/comm_wrap.cpp tests/ccu/test_tilexr_ccu_public_comm_api.py tests/ccu/test_tilexr_ccu_public_api_compile_probe.py +git rm tests/ccu/ccu_public_direct_api_compile_probe.c +git commit -m "refactor: remove public direct CCU API" +``` + +--- + +### Task 4: Introduce `TileXRCcuBackend` Facade And Move CCU State Out Of `TileXRComm` + +**Files:** +- Create: `src/comm/ccu/tilexr_ccu_backend.h` +- Create: `src/comm/ccu/tilexr_ccu_backend.cpp` +- Modify: `src/comm/tilexr_comm.h` +- Modify: `src/comm/tilexr_comm.cpp` +- Modify: `src/comm/CMakeLists.txt` +- Create: `tests/ccu/test_tilexr_ccu_backend_boundary.py` + +**Interfaces:** +- Produces: + - `class TileXRCcuBackend` + - `struct TileXRCcuBackendOptions` + - `TileXRComm::GetCcuBackendForCollectives()` + - `TileXRComm::EnableCcuBackendForTest()` +- Consumes: + - Existing CCU implementation headers under `src/comm/ccu` + +- [ ] **Step 1: Write backend-boundary source guard** + +Create `tests/ccu/test_tilexr_ccu_backend_boundary.py`: + +```python +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +COMM_HEADER = REPO_ROOT / "src" / "comm" / "tilexr_comm.h" +BACKEND_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_backend.h" +BACKEND_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_backend.cpp" + + +class TileXRCcuBackendBoundaryTest(unittest.TestCase): + def test_backend_files_exist(self): + self.assertTrue(BACKEND_HEADER.exists()) + self.assertTrue(BACKEND_SOURCE.exists()) + + def test_tilexr_comm_header_owns_only_opaque_backend(self): + header = COMM_HEADER.read_text(encoding="utf-8") + self.assertIn("class TileXRCcuBackend;", header) + self.assertIn("std::unique_ptr ccuBackend_", header) + for needle in [ + "tilexr_ccu_direct_orchestrator.h", + "tilexr_ccu_direct_runtime.h", + "tilexr_ccu_lower_layer_plan_builder.h", + "TileXRCcuDirectRuntime", + "directCcuBasicInfo_", + "directCcuLowerLayerPlan_", + "directCcuVerifiedEndpointRoutes_", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + + def test_backend_header_exposes_facade_not_public_c_api(self): + header = BACKEND_HEADER.read_text(encoding="utf-8") + self.assertIn("class TileXRCcuBackend", header) + self.assertIn("struct TileXRCcuBackendOptions", header) + self.assertIn("PrepareCollective", header) + self.assertIn("SubmitCollective", header) + for needle in [ + "TileXRDirectCcuPreparedTasksPtr", + "TileXRCommPrepareDirectCcu", + "TileXRDirectCcuSubmitPrepared", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + + +if __name__ == "__main__": + unittest.main() +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +``` + +Expected: failure because backend files do not exist and `TileXRComm` still exposes direct CCU state. + +- [ ] **Step 3: Add backend facade header** + +Create `src/comm/ccu/tilexr_ccu_backend.h`: + +```cpp +#ifndef TILEXR_CCU_BACKEND_H +#define TILEXR_CCU_BACKEND_H + +#include +#include +#include +#include + +#include "acl/acl_base.h" +#include "tilexr_types.h" + +namespace TileXR { + +class TileXRComm; +class TileXRCcuRuntimeSession; +class TileXRCcuCollectivePlanner; +class TileXRCcuExecutor; + +struct TileXRCcuBackendOptions { + int rank = 0; + int rankSize = 0; + int devId = 0; + std::string uid; + TileXRComm *comm = nullptr; +}; + +struct TileXRCcuCollectiveRequest { + TileXRType type = TileXRType::ALL_GATHER; + void *sendBuf = nullptr; + void *recvBuf = nullptr; + int64_t count = 0; + TileXRDataType dataType = TILEXR_DATA_TYPE_RESERVED; + TileXRReduceOp reduceOp = TILEXR_REDUCE_RESERVED; + int root = 0; + aclrtStream stream = nullptr; +}; + +struct TileXRCcuCollectivePlan { + bool ready = false; +}; + +class TileXRCcuBackend { +public: + TileXRCcuBackend(); + ~TileXRCcuBackend(); + + TileXRCcuBackend(const TileXRCcuBackend&) = delete; + TileXRCcuBackend& operator=(const TileXRCcuBackend&) = delete; + + int Init(const TileXRCcuBackendOptions &options); + void Shutdown(); + bool Available() const; + bool Supports(const TileXRCcuCollectiveRequest &request) const; + int PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan); + int SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream); + +private: + TileXRCcuBackendOptions options_; + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_BACKEND_H +``` + +- [ ] **Step 4: Add minimal backend source** + +Create `src/comm/ccu/tilexr_ccu_backend.cpp`: + +```cpp +#include "ccu/tilexr_ccu_backend.h" + +namespace TileXR { + +TileXRCcuBackend::TileXRCcuBackend() = default; +TileXRCcuBackend::~TileXRCcuBackend() +{ + Shutdown(); +} + +int TileXRCcuBackend::Init(const TileXRCcuBackendOptions &options) +{ + options_ = options; + initialized_ = true; + return TILEXR_SUCCESS; +} + +void TileXRCcuBackend::Shutdown() +{ + initialized_ = false; +} + +bool TileXRCcuBackend::Available() const +{ + return initialized_; +} + +bool TileXRCcuBackend::Supports(const TileXRCcuCollectiveRequest &request) const +{ + return initialized_ && request.type == TileXRType::ALL_GATHER; +} + +int TileXRCcuBackend::PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan) +{ + if (plan == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!initialized_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (!Supports(request)) { + return TILEXR_ERROR_NOT_SUPPORT; + } + *plan = TileXRCcuCollectivePlan {}; + plan->ready = true; + return TILEXR_SUCCESS; +} + +int TileXRCcuBackend::SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream) +{ + if (!initialized_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + return plan.ready ? TILEXR_SUCCESS : TILEXR_ERROR_PARA_CHECK_FAIL; +} + +} // namespace TileXR +``` + +- [ ] **Step 5: Change `TileXRComm` ownership to opaque backend** + +In `src/comm/tilexr_comm.h`, remove CCU private includes: + +```cpp +#include "ccu/tilexr_ccu_direct_orchestrator.h" +#include "ccu/tilexr_ccu_direct_runtime.h" +#include "ccu/tilexr_ccu_lower_layer_plan_builder.h" +``` + +Add forward declaration in namespace `TileXR`: + +```cpp +class TileXRCcuBackend; +``` + +Replace direct CCU public/private method declarations with: + +```cpp +int InitCcuBackend(); +TileXRCcuBackend *GetCcuBackendForCollectives(); +const TileXRCcuBackend *GetCcuBackendForCollectives() const; +``` + +Replace all direct CCU member fields with: + +```cpp +std::unique_ptr ccuBackend_; +``` + +- [ ] **Step 6: Wire `TileXRComm` implementation** + +In `src/comm/tilexr_comm.cpp`, include: + +```cpp +#include "ccu/tilexr_ccu_backend.h" +``` + +Add: + +```cpp +int TileXRComm::InitCcuBackend() +{ + if (ccuBackend_ == nullptr) { + ccuBackend_.reset(new (std::nothrow) TileXRCcuBackend()); + if (ccuBackend_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + } + TileXRCcuBackendOptions options {}; + options.rank = rank_; + options.rankSize = rankSize_; + options.devId = devId_; + options.uid = uid_; + options.comm = this; + return ccuBackend_->Init(options); +} + +TileXRCcuBackend *TileXRComm::GetCcuBackendForCollectives() +{ + return ccuBackend_.get(); +} + +const TileXRCcuBackend *TileXRComm::GetCcuBackendForCollectives() const +{ + return ccuBackend_.get(); +} +``` + +For this task, delete the old direct CCU methods from `TileXRComm` or move their bodies into `tilexr_ccu_backend.cpp` behind private helper functions. Keep `Init()` and `InitThread()` compiling by replacing direct CCU runtime init calls with `InitCcuBackend()` only when the new generic config says CCU is enabled. + +- [ ] **Step 7: Add source to build** + +In `src/comm/CMakeLists.txt`, add: + +```cmake + ccu/tilexr_ccu_backend.h + ccu/tilexr_ccu_backend.cpp +``` + +- [ ] **Step 8: Run source guard and build** + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +source scripts/common_env.sh +cmake --build build --target tile-comm -j$(nproc) +``` + +Expected: source guard passes and `tile-comm` builds. + +- [ ] **Step 9: Commit** + +```bash +git add src/comm/ccu/tilexr_ccu_backend.h src/comm/ccu/tilexr_ccu_backend.cpp src/comm/tilexr_comm.h src/comm/tilexr_comm.cpp src/comm/CMakeLists.txt tests/ccu/test_tilexr_ccu_backend_boundary.py +git commit -m "refactor: introduce internal CCU backend" +``` + +--- + +### Task 5: Split CCU Backend Internals Into Runtime Session, Planner, And Executor + +**Files:** +- Create: `src/comm/ccu/tilexr_ccu_runtime_session.h` +- Create: `src/comm/ccu/tilexr_ccu_runtime_session.cpp` +- Create: `src/comm/ccu/tilexr_ccu_collective_planner.h` +- Create: `src/comm/ccu/tilexr_ccu_collective_planner.cpp` +- Create: `src/comm/ccu/tilexr_ccu_executor.h` +- Create: `src/comm/ccu/tilexr_ccu_executor.cpp` +- Modify: `src/comm/ccu/tilexr_ccu_backend.h` +- Modify: `src/comm/ccu/tilexr_ccu_backend.cpp` +- Modify: `src/comm/CMakeLists.txt` +- Modify: `tests/ccu/test_tilexr_ccu_backend_boundary.py` + +**Interfaces:** +- Consumes: + - `TileXRCcuBackend` + - existing direct CCU lower-level files. +- Produces: + - `TileXRCcuRuntimeSession` + - `TileXRCcuCollectivePlanner` + - `TileXRCcuExecutor` + +- [ ] **Step 1: Extend boundary test** + +Append to `test_tilexr_ccu_backend_boundary.py`: + +```python + def test_backend_internals_are_split(self): + expected = [ + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_runtime_session.h", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_runtime_session.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_collective_planner.h", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_collective_planner.cpp", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_executor.h", + REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_executor.cpp", + ] + for path in expected: + with self.subTest(path=path.name): + self.assertTrue(path.exists()) +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +``` + +Expected: failures for missing split files. + +- [ ] **Step 3: Add runtime session** + +Create `src/comm/ccu/tilexr_ccu_runtime_session.h`: + +```cpp +#ifndef TILEXR_CCU_RUNTIME_SESSION_H +#define TILEXR_CCU_RUNTIME_SESSION_H + +#include "ccu/tilexr_ccu_backend.h" + +namespace TileXR { + +class TileXRCcuRuntimeSession { +public: + int Init(const TileXRCcuBackendOptions &options); + void Shutdown(); + bool Available() const; + +private: + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_RUNTIME_SESSION_H +``` + +Create `src/comm/ccu/tilexr_ccu_runtime_session.cpp`: + +```cpp +#include "ccu/tilexr_ccu_runtime_session.h" + +namespace TileXR { + +int TileXRCcuRuntimeSession::Init(const TileXRCcuBackendOptions&) +{ + initialized_ = true; + return TILEXR_SUCCESS; +} + +void TileXRCcuRuntimeSession::Shutdown() +{ + initialized_ = false; +} + +bool TileXRCcuRuntimeSession::Available() const +{ + return initialized_; +} + +} // namespace TileXR +``` + +- [ ] **Step 4: Add collective planner** + +Create `src/comm/ccu/tilexr_ccu_collective_planner.h`: + +```cpp +#ifndef TILEXR_CCU_COLLECTIVE_PLANNER_H +#define TILEXR_CCU_COLLECTIVE_PLANNER_H + +#include "ccu/tilexr_ccu_backend.h" + +namespace TileXR { + +class TileXRCcuRuntimeSession; + +class TileXRCcuCollectivePlanner { +public: + bool Supports(const TileXRCcuRuntimeSession &session, const TileXRCcuCollectiveRequest &request) const; + int Prepare(const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request, + TileXRCcuCollectivePlan *plan) const; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_COLLECTIVE_PLANNER_H +``` + +Create `src/comm/ccu/tilexr_ccu_collective_planner.cpp`: + +```cpp +#include "ccu/tilexr_ccu_collective_planner.h" + +#include "ccu/tilexr_ccu_runtime_session.h" + +namespace TileXR { + +bool TileXRCcuCollectivePlanner::Supports( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request) const +{ + return session.Available() && request.type == TileXRType::ALL_GATHER; +} + +int TileXRCcuCollectivePlanner::Prepare( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request, + TileXRCcuCollectivePlan *plan) const +{ + if (plan == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!session.Available()) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (!Supports(session, request)) { + return TILEXR_ERROR_NOT_SUPPORT; + } + *plan = TileXRCcuCollectivePlan {}; + plan->ready = true; + return TILEXR_SUCCESS; +} + +} // namespace TileXR +``` + +- [ ] **Step 5: Add executor** + +Create `src/comm/ccu/tilexr_ccu_executor.h`: + +```cpp +#ifndef TILEXR_CCU_EXECUTOR_H +#define TILEXR_CCU_EXECUTOR_H + +#include "acl/acl_base.h" +#include "ccu/tilexr_ccu_backend.h" + +namespace TileXR { + +class TileXRCcuRuntimeSession; + +class TileXRCcuExecutor { +public: + int Submit(const TileXRCcuRuntimeSession &session, const TileXRCcuCollectivePlan &plan, aclrtStream stream) const; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_EXECUTOR_H +``` + +Create `src/comm/ccu/tilexr_ccu_executor.cpp`: + +```cpp +#include "ccu/tilexr_ccu_executor.h" + +#include "ccu/tilexr_ccu_runtime_session.h" + +namespace TileXR { + +int TileXRCcuExecutor::Submit( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectivePlan &plan, + aclrtStream) +{ + if (!session.Available()) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + return plan.ready ? TILEXR_SUCCESS : TILEXR_ERROR_PARA_CHECK_FAIL; +} + +} // namespace TileXR +``` + +- [ ] **Step 6: Refactor backend facade to delegate** + +In `tilexr_ccu_backend.h`, replace the `bool initialized_` member with: + +```cpp +std::unique_ptr runtimeSession_; +std::unique_ptr planner_; +std::unique_ptr executor_; +``` + +In `tilexr_ccu_backend.cpp`, include the three new headers and change the methods: + +```cpp +TileXRCcuBackend::TileXRCcuBackend() + : runtimeSession_(new (std::nothrow) TileXRCcuRuntimeSession()), + planner_(new (std::nothrow) TileXRCcuCollectivePlanner()), + executor_(new (std::nothrow) TileXRCcuExecutor()) +{ +} + +int TileXRCcuBackend::Init(const TileXRCcuBackendOptions &options) +{ + if (runtimeSession_ == nullptr || planner_ == nullptr || executor_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + options_ = options; + return runtimeSession_->Init(options); +} + +void TileXRCcuBackend::Shutdown() +{ + if (runtimeSession_ != nullptr) { + runtimeSession_->Shutdown(); + } +} + +bool TileXRCcuBackend::Available() const +{ + return runtimeSession_ != nullptr && runtimeSession_->Available(); +} + +bool TileXRCcuBackend::Supports(const TileXRCcuCollectiveRequest &request) const +{ + return runtimeSession_ != nullptr && planner_ != nullptr && planner_->Supports(*runtimeSession_, request); +} + +int TileXRCcuBackend::PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan) +{ + if (runtimeSession_ == nullptr || planner_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return planner_->Prepare(*runtimeSession_, request, plan); +} + +int TileXRCcuBackend::SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream) +{ + if (runtimeSession_ == nullptr || executor_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return executor_->Submit(*runtimeSession_, plan, stream); +} +``` + +- [ ] **Step 7: Add files to CMake and run build** + +In `src/comm/CMakeLists.txt`, add all six new files. + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +source scripts/common_env.sh +cmake --build build --target tile-comm -j$(nproc) +``` + +Expected: source guard passes and `tile-comm` builds. + +- [ ] **Step 8: Move existing CCU logic into split classes** + +Move existing code from `TileXRComm` and current CCU files into the split classes with these ownership rules: + +```text +TileXRCcuRuntimeSession: + InitDirectCcuRuntime + RefreshDirectCcuBasicInfo + RegisterCcuResourceRmaBuffer + ExportRemoteCcuRmaBuffers + lower-layer transport exchange + +TileXRCcuCollectivePlanner: + PrepareDirectCcuInstallAttempt + PrepareDirectCcuMemoryCopyInstallAttempt only under TILEXR_CCU_TESTING + FillDirectCcuLowerLayerPlanFromAllocation + lower-layer install plan generation + +TileXRCcuExecutor: + TileXRCcuSubmitPreparedTasks + runtime submit/report mapping + instruction readback only under TILEXR_CCU_TESTING +``` + +After each moved method, run: + +```bash +source scripts/common_env.sh +cmake --build build --target tile-comm -j$(nproc) +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +``` + +Expected: build and source guard continue to pass. + +- [ ] **Step 9: Commit** + +```bash +git add src/comm/ccu/tilexr_ccu_runtime_session.h src/comm/ccu/tilexr_ccu_runtime_session.cpp src/comm/ccu/tilexr_ccu_collective_planner.h src/comm/ccu/tilexr_ccu_collective_planner.cpp src/comm/ccu/tilexr_ccu_executor.h src/comm/ccu/tilexr_ccu_executor.cpp src/comm/ccu/tilexr_ccu_backend.h src/comm/ccu/tilexr_ccu_backend.cpp src/comm/CMakeLists.txt tests/ccu/test_tilexr_ccu_backend_boundary.py +git commit -m "refactor: split CCU backend internals" +``` + +--- + +### Task 6: Connect Forced CCU/UDMA Dispatch To Real Backends And Verify Guards + +**Files:** +- Modify: `src/collectives/host/collective_backend.cpp` +- Modify: `src/collectives/host/tilexr_collectives.cpp` +- Modify: `src/comm/tilexr_comm.h` +- Modify: `src/comm/tilexr_comm.cpp` +- Modify: `tests/collectives/unit/test_tilexr_collective_backend_options.cpp` +- Modify: `tests/ccu/test_tilexr_ccu_public_comm_api.py` + +**Interfaces:** +- Consumes: + - `TileXRComm::GetCcuBackendForCollectives()` + - `TileXRCcuBackend::PrepareCollective` + - `TileXRCcuBackend::SubmitCollective` +- Produces: + - Real forced `CCU` dispatch path. + - Real forced `UDMA` error path until UDMA-backed collectives are implemented. + +- [ ] **Step 1: Add test assertions for forced modes** + +In `tests/collectives/unit/test_tilexr_collective_backend_options.cpp`, add cases: + +```cpp + state.ccuInitialized = true; + state.ccuSupported = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_ERROR_NOT_SUPPORT) { + return 6; + } + + state.ccuSupported = true; + state.ccuReturn = TileXR::TILEXR_SUCCESS; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_SUCCESS) { + return 7; + } +``` + +- [ ] **Step 2: Run test** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options -j$(nproc) +ctest --test-dir build -R test_tilexr_collective_backend_options --output-on-failure +``` + +Expected: test passes in fake-state mode. + +- [ ] **Step 3: Implement real CCU dispatch when fake state is disabled** + +In `collective_backend.cpp`, include `tilexr_comm.h` and `ccu/tilexr_ccu_backend.h`. + +Update `DispatchCcu`: + +```cpp +int DispatchCcu(const CollectiveRequest &request) +{ + if (g_testState.enabled) { + if (!g_testState.ccuInitialized) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return g_testState.ccuSupported ? g_testState.ccuReturn : TileXR::TILEXR_ERROR_NOT_SUPPORT; + } + + auto *comm = static_cast(request.comm); + if (comm == nullptr || comm->GetCcuBackendForCollectives() == nullptr) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + TileXR::TileXRCcuBackend *backend = comm->GetCcuBackendForCollectives(); + TileXR::TileXRCcuCollectiveRequest ccuRequest {}; + ccuRequest.type = request.type; + ccuRequest.sendBuf = request.sendBuf; + ccuRequest.recvBuf = request.recvBuf; + ccuRequest.count = request.count; + ccuRequest.dataType = request.dataType; + ccuRequest.reduceOp = request.reduceOp; + ccuRequest.root = request.root; + ccuRequest.stream = request.stream; + + TileXR::TileXRCcuCollectivePlan plan {}; + const int prepareRet = backend->PrepareCollective(ccuRequest, &plan); + if (prepareRet != TileXR::TILEXR_SUCCESS) { + return prepareRet; + } + return backend->SubmitCollective(plan, request.stream); +} +``` + +- [ ] **Step 4: Keep UDMA forced mode explicit** + +Until a UDMA-backed collective path exists, keep `DispatchUdma` returning: + +```cpp +return TileXR::TILEXR_ERROR_NOT_SUPPORT; +``` + +when UDMA is initialized but no matching collective backend exists. Do not route to AIV. + +- [ ] **Step 5: Run focused tests and build** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target tilexr-collectives test_tilexr_collective_backend_options -j$(nproc) +ctest --test-dir build -R test_tilexr_collective_backend_options --output-on-failure +python3 tests/ccu/test_tilexr_ccu_public_comm_api.py +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +``` + +Expected: build passes and all listed tests pass. + +- [ ] **Step 6: Commit** + +```bash +git add src/collectives/host/collective_backend.cpp src/collectives/host/tilexr_collectives.cpp src/comm/tilexr_comm.h src/comm/tilexr_comm.cpp tests/collectives/unit/test_tilexr_collective_backend_options.cpp tests/ccu/test_tilexr_ccu_public_comm_api.py +git commit -m "feat: connect CCU collective backend dispatch" +``` + +--- + +### Task 7: Final Verification + +**Files:** +- No new files. +- Verify all files touched in Tasks 1-6. + +**Interfaces:** +- Consumes all previous task outputs. +- Produces verified refactor state. + +- [ ] **Step 1: Run public surface guards** + +Run: + +```bash +rg -n "CCU|Ccu|DirectCcu|TILEXR_DIRECT_CCU" src/include/tilexr_api.h +``` + +Expected: no output and exit code `1`. + +Run: + +```bash +python3 tests/ccu/test_tilexr_ccu_public_comm_api.py +python3 tests/ccu/test_tilexr_ccu_public_api_compile_probe.py +python3 tests/ccu/test_tilexr_ccu_backend_boundary.py +``` + +Expected: all pass. + +- [ ] **Step 2: Run collective backend tests** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target test_tilexr_collective_backend_options test_tilexr_collectives_header_compile test_tilexr_collectives_api -j$(nproc) +ctest --test-dir build -R "test_tilexr_collective_backend_options|test_tilexr_collectives_header_compile|test_tilexr_collectives_api" --output-on-failure +``` + +Expected: all listed tests pass. + +- [ ] **Step 3: Build core libraries** + +Run: + +```bash +source scripts/common_env.sh +cmake --build build --target tile-comm tilexr-collectives -j$(nproc) +``` + +Expected: both targets build. + +- [ ] **Step 4: Inspect remaining CCU leakage** + +Run: + +```bash +rg -n "TileXRDirectCcu|PrepareDirectCcu|SubmitPrepared|TILEXR_DIRECT_CCU|TileXRCommInitRankDirectCcu" src tests +``` + +Expected: matches only in intentionally retained internal CCU implementation tests guarded by `TILEXR_CCU_TESTING`, or no matches after test migration. No matches in installed public headers or `comm_wrap.cpp`. + +- [ ] **Step 5: Confirm no verification-only changes remain** + +Run: + +```bash +git status --short +``` + +Expected: no output. If this command lists files, inspect them with `git diff` and either commit the intentional fix +with the exact files shown by `git status --short`, or revert generated artifacts that are not source changes. diff --git a/docs/superpowers/specs/2026-07-06-ccu-api-isolation-design.md b/docs/superpowers/specs/2026-07-06-ccu-api-isolation-design.md new file mode 100644 index 00000000..fe4b478e --- /dev/null +++ b/docs/superpowers/specs/2026-07-06-ccu-api-isolation-design.md @@ -0,0 +1,237 @@ +# TileXR CCU API Isolation Design + +## Context + +The `codex/direct-ccu-rebased` branch currently implements direct CCU support under `src/comm/ccu`, but the public and +core communication surfaces are still polluted by CCU-specific declarations: + +- `src/include/tilexr_api.h` declares direct CCU handles, constants, option structs, report structs, task structs, and + entry points. +- `src/comm/comm_wrap.cpp` contains both generic TileXR C API wrappers and direct CCU public API bridge code. +- `src/comm/tilexr_comm.h` directly includes multiple CCU internal headers and exposes CCU-specific methods and state + on `TileXRComm`. + +This makes direct CCU look like part of the baseline TileXR communication API. The desired model is that CCU remains +available in the same `tile-comm` library, but it is an internal communication backend. Users should only enable the +backend during communicator setup and select it through normal collective communication controls. They should not call +CCU resource, repository, task-prepare, submit, or readback APIs directly. + +## Goals + +- Remove all CCU-specific public declarations from `tilexr_api.h`. +- Remove the direct CCU public C API bridge from `comm_wrap.cpp`. +- Hide CCU runtime state and orchestration behind a CCU-owned context instead of exposing it directly on + `TileXRComm`. +- Model CCU as an internal C++ backend class rather than a user-facing C API. +- Add a generic collective backend selection surface so users can request `AUTO`, `AIV`, `UDMA`, or `CCU` collective + execution without seeing backend resource details. +- Keep the build and link model simple: direct CCU stays in `libtile-comm.so`; no separate shared library is introduced + in this change. +- Preserve current direct CCU behavior for internal probes and hardware validation through CCU-owned test hooks or + internal C++ helpers. + +## Non-Goals + +- Do not split CCU into a new library or optional package target. +- Do not redesign the CCU lower-layer resource allocation, repository install, or runtime launch algorithms. +- Do not promote direct CCU prepare/submit/readback operations as public user APIs in this pass. +- Do not introduce a generic backend manager abstraction before there is more than one optional collective backend. +- Do not change non-CCU TileXR communication, UDMA, SDMA, collectives, or EP behavior. + +## Public API Layout + +`src/include/tilexr_api.h` remains the baseline TileXR C API header. It should contain only generic communication, +UDMA, SDMA, DFX, and common lifecycle declarations. It must not contain these strings: + +- `CCU` +- `Ccu` +- `DirectCcu` +- `TILEXR_DIRECT_CCU` + +No installed `tilexr_ccu_api.h` is introduced. A CCU-specific public header would be premature because the intended +user flow does not include direct CCU task preparation or submission. + +The only user-visible direction for CCU should be through generic runtime configuration surfaces: + +- communicator initialization can enable optional backends without exposing CCU resource structs or task descriptors; +- collective calls can select a communication mode/backend using the collective API surface, not a direct CCU API. + +To keep `tilexr_api.h` free of CCU symbols, communicator initialization should enable the CCU backend through one of +these generic mechanisms: + +- an environment/config string such as a backend allowlist; or +- a future generic init-options API whose header names do not encode CCU-specific task concepts. + +Typed CCU selection belongs in the collective API layer. A future collective options surface can live in +`tilexr_collectives.h`, for example: + +```cpp +enum TileXRCollectiveBackend { + TILEXR_COLLECTIVE_BACKEND_AUTO = 0, + TILEXR_COLLECTIVE_BACKEND_AIV = 1, + TILEXR_COLLECTIVE_BACKEND_UDMA = 2, + TILEXR_COLLECTIVE_BACKEND_CCU = 3, +}; + +struct TileXRCollectiveOptions { + /* Null or zero-initialized options select AUTO. */ + TileXRCollectiveBackend backend = TILEXR_COLLECTIVE_BACKEND_AUTO; +}; + +int TileXRAllGatherEx( + void* sendBuf, + void* recvBuf, + int64_t sendCount, + TileXR::TileXRDataType dataType, + TileXRCommPtr comm, + aclrtStream stream, + const TileXRCollectiveOptions* options); +``` + +The exact naming can change during implementation, but the public surface must expose only backend selection and must +not expose repository, SQE, XN, CKE, instruction, or task-preparation details. + +If the implementation needs a temporary internal seam for smoke probes, it should live under `src/comm/ccu` or +`tests/ccu`, should not be installed, and should not be documented as user API. + +## Source Layout + +The generic wrapper file, `src/comm/comm_wrap.cpp`, keeps only baseline TileXR API implementations. Direct CCU +prepare/submit/readback C wrappers are removed from this file instead of moved to an installed CCU header. + +The CCU source tree owns internal C++ entry points for backend use and validation. These entry points should be +organized around a class, not a broad C API facade. + +Because CCU is currently the only new backend in this change that needs a dedicated runtime context, `TileXRComm` can +own a single CCU backend context directly. Do not add a generic `TileXRCommBackends` manager in this pass. `AIV` maps +to the existing AIV/default collective path, and `UDMA` maps to existing UDMA-capable collective paths when available. +If a later backend needs the same lifecycle pattern as CCU, that will be the right time to introduce a manager. + +## `TileXRComm` Boundary + +`TileXRComm` should stop exposing CCU internals as public methods and direct member fields. A CCU-owned backend class +should hold the direct CCU runtime state currently stored on `TileXRComm`, including: + +- `TileXRCcuDirectRuntime` +- cached basic info and reports +- lower-layer template, snapshot, plan, routes, and plan reports +- direct CCU allgather round state +- prepare/install/readback helper methods used by collectives or internal validation + +The preferred implementation is `src/comm/ccu/tilexr_ccu_backend.{h,cpp}` with a class named `TileXRCcuBackend`. +`TileXRComm` owns this context as an opaque `std::unique_ptr` and exposes only narrow internal accessors needed by +communicator initialization and collective dispatch, for example: + +- initialize or disable the CCU backend according to communicator configuration; +- query whether the CCU backend is available; +- dispatch a collective operation through the CCU backend when the collective layer selects that mode; +- pass rank, rank size, device id, uid, and socket-exchange facilities needed by the backend. + +If a temporary accessor is needed during migration, it must be clearly CCU-scoped and not added to `tilexr_api.h`. + +The backend class should expose cohesive methods rather than mirroring the old public C functions one-for-one. Example +shape: + +```cpp +class TileXRCcuBackend { +public: + int Init(const TileXRCcuBackendOptions& options); + void Shutdown(); + bool Available() const; + + int PrepareCollective(const TileXRCcuCollectiveRequest& request, TileXRCcuCollectivePlan* plan); + int SubmitCollective(const TileXRCcuCollectivePlan& plan, void* stream); + + int PrepareMemoryCopyForTest(const TileXRCcuMemoryCopyRequest& request, TileXRCcuPreparedTasks* tasks); + int ReadInstructionsForTest(...); +}; +``` + +The exact method names can differ, but the boundary should express backend lifecycle and collective execution rather +than a user-facing direct CCU API. + +## CCU Internal Structure + +`TileXRCcuBackend` is the facade used by `TileXRComm` and the collective dispatch layer. Internally it should separate +control-plane setup from collective execution: + +- `TileXRCcuRuntimeSession`: owns runtime availability, basic info refresh, driver adapter creation, RA/HCCP state, + lower-layer transport exchange, and shutdown. +- `TileXRCcuCollectivePlanner`: turns a typed collective request into CCU resource allocation, lower-layer install + plan, repository image, launch package, and submit task plan. +- `TileXRCcuExecutor`: submits prepared CCU tasks to a stream, handles synchronization/reporting policy, and maps + runtime failures to TileXR error codes. + +This split keeps the existing low-level implementation reusable while preventing `TileXRComm` from knowing about +resource repositories, SQE layout, XN/CKE allocation, or diagnostic readback. + +## Backend Selection And Fallback + +Collective dispatch must distinguish "enabled" from "selected". If `TileXRCollectiveOptions` is `nullptr` or +zero-initialized, the selected backend is `AUTO`. + +- `AUTO`: choose the best available supported backend without surfacing backend-specific errors for skipped candidates. + The default policy is CCU when the communicator enabled and initialized CCU and the requested collective is supported, + then UDMA when UDMA is initialized and supports the requested collective, then the existing AIV path. +- `AIV`: always use the existing AIV/default collective path and do not touch CCU backend state. It also must not require + UDMA. +- `UDMA`: require a UDMA-backed collective path. If UDMA was not initialized, return `TILEXR_ERROR_NOT_INITIALIZED`. + If UDMA is initialized but the requested collective, datatype, topology, rank count, or hardware state is unsupported, + return `TILEXR_ERROR_NOT_SUPPORT`. Do not silently fall back. +- `CCU`: require CCU. If the communicator did not enable or initialize the backend, return + `TILEXR_ERROR_NOT_INITIALIZED`. If CCU is initialized but the requested collective, datatype, topology, rank count, + or hardware state is unsupported, return `TILEXR_ERROR_NOT_SUPPORT`. Do not silently fall back. + +The old collective entry points keep their current behavior and are equivalent to `AUTO` unless a later compatibility +decision says otherwise. New `*Ex` entry points can carry explicit `TileXRCollectiveOptions`. + +## Test Hooks + +Diagnostic-only behavior such as memory-copy task preparation and instruction readback should not be normal backend +API. These helpers should be available only through one of these mechanisms: + +- `#if defined(TILEXR_CCU_TESTING)` declarations inside CCU implementation files; or +- private test helpers under `tests/ccu`. + +Production backend methods should be limited to lifecycle, capability query, collective planning, and collective +submission. + +## Build And Install + +`src/comm/CMakeLists.txt` should add the new CCU backend/context sources to `tile-comm`. + +Install headers should not include a CCU-specific public API header. + +No separate library or install component is introduced. + +## Tests + +Update the existing CCU tests so they encode the new boundary: + +- The baseline public header test asserts `tilexr_api.h` contains no CCU symbols. +- The old CCU public C API compile probe is removed or converted into an internal C++ backend probe. +- The smoke probe uses internal CCU backend/test hooks or the eventual generic collective-mode selection path; it does + not include an installed CCU API header. +- Existing checks that private hcomm/hccl symbols and runtime launch structs do not leak to installed public headers + are retained against `tilexr_api.h` and other installed headers. +- A source-guard asserts `comm_wrap.cpp` does not define direct CCU prepare/submit/readback wrappers. +- CCU implementation tests that previously looked for CCU methods directly on `TileXRComm` should be adjusted to the + new backend/context boundary. +- Collective API tests should verify default-unset `AUTO`, explicit `AUTO`, forced `AIV`, forced `UDMA`, and forced + `CCU` fallback/error behavior using fake backend states before relying on hardware smoke runs. + +## Acceptance Criteria + +- `rg -n "CCU|Ccu|DirectCcu|TILEXR_DIRECT_CCU" src/include/tilexr_api.h` returns no matches. +- No installed public header named `tilexr_ccu_api.h` is added. +- The old direct CCU public API compile probe is removed or no longer treats CCU as external user API. +- `comm_wrap.cpp` contains no direct CCU API bridge implementation. +- CCU state is no longer directly stored as many fields on `TileXRComm`; it is owned by the CCU backend/context class. +- There is no generic backend manager abstraction in this pass; `TileXRComm` owns only the CCU backend context. +- Installed public headers do not expose `TileXRDirectCcu*`, `PrepareDirectCcu`, `SubmitPrepared`, `Repository`, `SQE`, + `XN`, `CKE`, or CCU task descriptors. `tilexr_collectives.h` may expose high-level backend enum values for `AUTO`, + `AIV`, `UDMA`, and `CCU`. +- Unset collective options select `AUTO`. +- Forced `UDMA` and forced `CCU` collective modes do not silently fall back when the selected backend is unavailable or + unsupported. +- The focused CCU unit/source-guard tests pass. diff --git a/docs/superpowers/specs/2026-07-14-ccu-alltoall-mvp-design.md b/docs/superpowers/specs/2026-07-14-ccu-alltoall-mvp-design.md new file mode 100644 index 00000000..54d91812 --- /dev/null +++ b/docs/superpowers/specs/2026-07-14-ccu-alltoall-mvp-design.md @@ -0,0 +1,503 @@ +# TileXR Direct CCU AllToAll, Loop Reuse, and Signal/Wait Design + +## Status + +This document describes the direct CCU implementation in the current TileXR +tree. It replaces the original fixed two-rank MVP proposal. + +The implemented test surface provides: + +- a two-rank LocalToRemote AllToAll long mission; +- a runtime-sized AllToAll mesh for 2 through 64 ranks; +- repeated submission of one installed mission; +- internal two-rank signal, wait, and signal-and-wait plans; +- an opt-in hardware smoke runner with data and synchronization checks. + +The implementation is TileXR-owned. Active code does not include, call, or +link HCCL or hcomm private CCU interfaces. HCCL topology files and captured +microcode may be used as behavior and topology references only. + +## Scope and Boundaries + +The direct CCU path is currently an internal and test-oriented capability. +AllToAll preparation entry points used by the smoke probe are compiled under +`TILEXR_CCU_TESTING`. Signal/wait is exposed through the internal +`TileXRCcuBackend` C++ interface. No public collective or signal/wait C API +is added to `src/include/tilexr_api.h`. + +The current implementation uses host-expanded 32 KB copy blocks. Loop reuse +means repeated submission of one prepared CCU mission; it does not mean that a +CCU LoopEngine or LoopGroup traverses the payload. + +Hardware validation is narrower than the accepted software shape: + +- the mesh builder and planner accept 2 through 64 ranks; +- the smoke probe accepts 128 KB or 2 MB per mesh destination chunk; +- the two-rank smoke accepts 2 MB or 8 MB payloads; +- the maintained validation in this change covers 2-rank and 4-rank 2 MB + runs on Ascend950PR. + +## Components + +The feature reuses the direct CCU stack: + +- `TileXRCcuRuntimeSession`: rank, device, socket exchange, runtime and RA + lifecycle, resource-window registration, and endpoint allgather. +- `TileXRCcuCollectivePlanner`: endpoint import, peer resource exchange, + topology-aware route construction, and install-attempt preparation. +- `TileXRCcuResourceAllocator`: mission, repository, XN, GSA, CKE, channel, + and source-CKE allocation. +- `tilexr_ccu_alltoall_program`: two-rank and mesh microcode builders and + mesh binding validation. +- `tilexr_ccu_signal_wait_program`: Signal, Wait, and SignalAndWait program + generation. +- `TileXRCcuDirectOrchestrator`: launch package, repository image, lower + layer install, mission install, and prepared task generation. +- `run_tilexr_ccu_direct_smoke.sh` and + `ccu_tilexr_direct_smoke_probe.cpp`: opt-in hardware execution and result + validation. + +## Common Prepare and Submit Lifecycle + +All modes use the same ownership sequence: + +1. Initialize the direct CCU runtime for the selected logical and physical + device. +2. Refresh and decode CCU basic resource information for the install die. +3. Register the local CCU resource window through RA ctx. +4. Register or import operation buffers and exchange endpoint metadata. +5. Resolve peer EIDs and ports from `/etc/hccl_rootinfo.json` and its + referenced topology file. +6. Allocate mission, repository, XN, GSA, CKE, source CKE, and channel ranges. +7. Exchange peer-local XN/CKE ownership and build verified channel routes. +8. Install lower-layer PFE, channel, and jetty contexts. +9. Build and install the instruction repository, mission, key, and task + windows. +10. Expose prepared tasks only when required install surfaces are verified. +11. Submit through `rtCCULaunch` and wait through + `aclrtSynchronizeStreamWithTimeout`. + +One verified endpoint jetty may be shared by multiple logical channels. A +channel must not reference an invented jetty context. Peer notification uses +the peer's exchanged local wait CKE and channel-bound remote XN. + +Topology resolution selects CTP when the topology edge advertises `UB_CTP`; +otherwise it selects RTP. `TILEXR_CCU_DIRECT_FORCE_TP_TYPE=ctp|rtp` is a +diagnostic override. + +## Data Layout + +### Two-Rank Long Mission + +Each rank owns one send buffer and one receive buffer: + +```text +rank 0 send[bytes] -> rank 1 receive[bytes] +rank 1 send[bytes] -> rank 0 receive[bytes] +``` + +The program requires a nonzero 4 KB-aligned payload. The program builder +accepts `memSlicePerBlock` in `[1, 8]`; the maintained smoke path requires +eight 4 KB memory slices per block: + +```text +blockBytes = 4096 * 8 = 32768 +blockCount = bytes / 32768 +``` + +The current long-mission smoke defaults to 2 MB, so it emits 64 copy blocks. + +### Runtime-Sized Mesh + +For rank count `N` and per-destination chunk size `C`, each rank allocates +`N * C` bytes for both source and destination. + +```text +source[targetRank][chunkOffset] +destination[sourceRank][chunkOffset] +``` + +For every remote peer, rank `r` writes: + +```text +local source[peerRank] -> peer destination[r] +``` + +The self chunk uses local memory-to-memory transfer through local MS: + +```text +local source[r] -> local destination[r] +``` + +The mesh requires `2 <= N <= 64`, exactly `N - 1` unique peer routes, and +a chunk size divisible by 32 KB. + +## Two-Rank Long-Mission Microcode + +The two-rank launch package allocates three synchronization resources: + +1. copy and loop-marker route; +2. PreSync address/token route; +3. reserved post route. + +The installed smoke program intentionally enables PreSync and disables a +separate PostSync and finish instruction. Completion of every copy block is +already consumed in-order before stream completion. + +### PreSync and Loop Marker + +PreSync publishes three values: + +- SQE argument zero loop marker with mask `0x1`; +- local receive address with mask `0x2`; +- local receive token with mask `0x4`. + +The receiver waits for mask `0x7`. The marker instruction sequence is: + +```text +LoadSqeArgsToX(localMarkerXn, arg0) +SyncXn(remoteMarkerXn, localMarkerXn, channel, notifyCke, 0x1) +``` + +Address and token use `LoadImdToXn` plus `SyncXn`. The final PreSync +`SetCke` wait consumes all three presence bits. This prevents an old CKE +arrival from being accepted as the current loop. + +### Copy Blocks + +Each 32 KB block emits a seven-instruction LocalToRemote memory-copy program. +The completion CKE is consumed before the next block reuses it. For `B` +blocks, the current long mission contains: + +```text +instructions = 7 + 7 * B +``` + +The leading seven instructions are marker/address/token PreSync and its wait. +For 2 MB, `B = 64` and the mission contains 455 instructions. + +## N-Rank Mesh Microcode + +Let: + +```text +P = N - 1 # remote peer count +G = ceil(P / 16) # grouped completion CKE count +B = chunkBytes / 32768 # blocks per destination chunk +``` + +Each peer has one logical synchronization resource and one channel. Shared +local address, token, and length XNs are reused across peer channels. Remote +completion bits are grouped in sets of at most 16 because one CKE mask is +16 bits. + +### Mesh PreSync + +The program loads the local receive address and token once, initializes the +source CKE, then posts both values to every peer. It waits once per peer. + +```text +preSyncInstructions = 3 + 3 * P +``` + +Mesh PreSync does not use the two-rank SQE loop marker. + +### Mesh Copy + +For each block: + +- every remote peer contributes six transfer instructions; +- the self chunk contributes nine local-MS instructions; +- one completion wait is emitted for each group of at most 16 remote peers. + +```text +copyInstructionsPerBlock = 6 * P + 9 + G +``` + +Remote peer ordinal `i` sets bit `i % 16` in completion CKE group +`i / 16`. The builder validates that completion CKEs are unique and do not +overlap the source CKE. + +### Mesh PostSync and Total Size + +After all blocks, every peer receives one `SyncCke` completion notification +and every local peer wait CKE is consumed. One finish instruction closes the +program. + +```text +postSyncInstructions = 2 * P +totalInstructions = + (3 + 3 * P) + + B * (6 * P + 9 + G) + + 2 * P + + 1 +``` + +The resource request uses: + +- `P` synchronization resources and channels; +- `P` peer local-wait CKEs; +- `P` remote-notify CKEs; +- one source CKE plus `G` grouped completion CKEs; +- at least three local and three remote XNs; +- two local GSAs for the self copy. + +The builder performs a second binding validation pass over the encoded +program before repository installation. + +## Loop Reuse + +`TILEXR_CCU_ALLTOALL_LOOP_COUNT` controls repeated submission and defaults +to one. The probe accepts values from 1 through 1024. + +Preparation, buffer registration, lower-layer installation, repository +installation, and mission installation occur once. Each loop: + +1. fills source data with a rank-and-loop-specific pattern; +2. resets destination data to a loop-specific sentinel; +3. enters a host ready gate using `phase=loopIndex`; +4. submits the same prepared task and synchronizes the same stream; +5. validates the complete destination buffer; +6. enters a host done gate with the local result. + +For the two-rank long mission, each loop also: + +- writes a rank-and-loop marker to prepared task argument zero; +- reads the peer marker from the remote XN after stream completion; +- requires the exact expected marker before accepting the data result. + +The marker format is: + +```text +0x4343554c00000000 | (rank << 16) | loopIndex +``` + +For the mesh path, the current program has no SQE marker. Loop identity is +validated by source/destination data patterns and loop-specific host gates. +The probe additionally requires mission id, key, instruction range, task +shape, XN, CKE, and channel resources to remain identical after every loop +and prints `stableResources=1`. + +This is mission reuse, not LoopEngine execution. + +## Signal/Wait and Barrier + +Signal/wait is an internal two-rank backend feature: + +```cpp +TileXRCcuBackend::PrepareSignalWait(...) +TileXRCcuBackend::SubmitSignalWait(...) +``` + +`PrepareSignalWait` rejects any rank size other than two and requires the +other rank as `peerRank`. It allocates one synchronization resource and +uses the same direct runtime, endpoint exchange, lower-layer install, +repository, mission, and submit lifecycle as AllToAll. + +### Roles + +`TileXRCcuSignalWaitProgramRole` has three roles: + +- `Signal`: reserve prelude, source CKE initialization, peer `SyncCke`, + and finish. The emitted program has five instructions. +- `Wait`: one local CKE wait instruction. The allocated repository window + may be larger, but the installed task window is reduced to the actual + emitted program. +- `SignalAndWait`: Signal followed by a local wait and finish. The emitted + program has six instructions. + +The Signal role proves local post completion. The Wait role proves that the +peer signal reached its local wait CKE. SignalAndWait forms a two-rank +barrier when both ranks run it. + +### Smoke Role Selection + +With `TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT=1`, +`TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK` selects the Signal rank (default 0); +the other rank is Wait. + +With `TILEXR_CCU_DIRECT_SMOKE_BARRIER=1`, both ranks use SignalAndWait. The +barrier flag also enables the generic barrier-program override, so callers +should set `TILEXR_CCU_DIRECT_BARRIER_MODE` explicitly. The maintained CKE +form uses `sync_cke`. + +`TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK` and +`TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS` can delay one rank before +submit. Delaying the signaler provides timing evidence that the waiter's +stream blocks on the device-side event rather than a host marker. + +## Hardware Runner Safety + +`tests/ccu/run_tilexr_ccu_direct_smoke.sh` is safe by default. It exits +without touching ACL or NPU runtime unless: + +```text +TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 +``` + +When enabled, the runner: + +- checks `npu-smi info`; +- rejects selected busy or unhealthy devices by default; +- compiles the private smoke probe against `libtile-comm.so`; +- launches one rank process per selected device; +- installs CCU resources; +- submits real CCU tasks only when + `TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1`; +- enforces per-process and stream timeouts; +- validates result counts before reporting success. + +`TILEXR_CCU_SMOKE_ALLOW_UNHEALTHY_NPU=1` permits Warning/Alarm devices but +still rejects busy devices. It must be used only with explicit authorization. + +## Usage + +The examples assume `tile-comm` is already built and use the b110 CANN +package on the validation server. + +### Two-Rank AllToAll With Loop Reuse + +```bash +cd /home/TileXR +source /home/pkg/b110/cann-9.1.0/set_env.sh + +TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 \ +TILEXR_TILE_COMM_LIB=/home/TileXR/build_ccu_merge_abe4bd38/src/comm/libtile-comm.so \ +TILEXR_CCU_SMOKE_DEVICES=6,7 \ +TILEXR_CCU_RANK_SIZE=2 \ +TILEXR_CCU_DIRECT_SMOKE_ALLTOALL=1 \ +TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION=1 \ +TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ +TILEXR_CCU_ALLTOALL_BYTES=2097152 \ +TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP=8 \ +TILEXR_CCU_ALLTOALL_LOOP_COUNT=10 \ +TILEXR_CCU_SMOKE_TIMEOUT=180 \ +TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=12000 \ +timeout 220s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +Success requires 20 passing loop results, 20 matched peer markers, and zero +data mismatches. + +### Four-Rank Mesh + +```bash +cd /home/TileXR +source /home/pkg/b110/cann-9.1.0/set_env.sh + +TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 \ +TILEXR_TILE_COMM_LIB=/home/TileXR/build_ccu_merge_abe4bd38/src/comm/libtile-comm.so \ +TILEXR_CCU_SMOKE_DEVICES=0,1,2,3 \ +TILEXR_CCU_RANK_SIZE=4 \ +TILEXR_CCU_DIRECT_SMOKE_ALLTOALL=1 \ +TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH=1 \ +TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ +TILEXR_CCU_ALLTOALL_BYTES=2097152 \ +TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP=8 \ +TILEXR_CCU_ALLTOALL_LOOP_COUNT=1 \ +TILEXR_CCU_SMOKE_TIMEOUT=240 \ +TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=12000 \ +timeout 280s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +For loop count one, success requires four passing results, zero data +mismatches, and `stableResources=1` on every rank. + +### One-Way Signal/Wait + +```bash +cd /home/TileXR +source /home/pkg/b110/cann-9.1.0/set_env.sh + +TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 \ +TILEXR_TILE_COMM_LIB=/home/TileXR/build_ccu_merge_abe4bd38/src/comm/libtile-comm.so \ +TILEXR_CCU_SMOKE_DEVICES=6,7 \ +TILEXR_CCU_RANK_SIZE=2 \ +TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT=1 \ +TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK=0 \ +TILEXR_CCU_DIRECT_SMOKE_DELAY_RANK=0 \ +TILEXR_CCU_DIRECT_SMOKE_PRE_SUBMIT_DELAY_MS=1000 \ +TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ +TILEXR_CCU_SMOKE_TIMEOUT=120 \ +TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=12000 \ +timeout 150s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +Rank 0 is Signal and rank 1 is Wait. Reverse the direction by setting +`TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK=1`. + +### Two-Rank Barrier + +```bash +cd /home/TileXR +source /home/pkg/b110/cann-9.1.0/set_env.sh + +TILEXR_RUN_CCU_DIRECT_SMOKE_PROBE=1 \ +TILEXR_TILE_COMM_LIB=/home/TileXR/build_ccu_merge_abe4bd38/src/comm/libtile-comm.so \ +TILEXR_CCU_SMOKE_DEVICES=6,7 \ +TILEXR_CCU_RANK_SIZE=2 \ +TILEXR_CCU_DIRECT_SMOKE_BARRIER=1 \ +TILEXR_CCU_DIRECT_BARRIER_MODE=sync_cke \ +TILEXR_CCU_DIRECT_SMOKE_SUBMIT=1 \ +TILEXR_CCU_SMOKE_TIMEOUT=120 \ +TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=12000 \ +timeout 150s bash tests/ccu/run_tilexr_ccu_direct_smoke.sh +``` + +## Validation and Evidence + +Source-level coverage checks: + +```bash +python3 -m unittest \ + tests.ccu.test_tilexr_ccu_alltoall_program \ + tests.ccu.test_tilexr_ccu_barrier_program \ + tests.ccu.test_tilexr_ccu_direct_orchestrator \ + tests.ccu.test_tilexr_ccu_direct_smoke_probe \ + tests.ccu.test_tilexr_ccu_direct_smoke_runner \ + tests.ccu.test_tilexr_ccu_lower_layer_plan_builder \ + tests.ccu.test_tilexr_ccu_resource_allocator \ + tests.ccu.test_tilexr_ccu_signal_wait_program + +bash tests/ccu/check_tile_comm_no_hcomm_deps.sh \ + build_ccu_direct/src/comm/libtile-comm.so +``` + +Maintained hardware evidence for the current change: + +- 2-rank, devices 6 and 7, 2 MB long mission: both ranks returned + `syncRet=0`, peer markers matched, and `mismatches=0`. +- 4-rank, devices 0 through 3, 2 MB mesh: all ranks returned `syncRet=0`, + `mismatches=0`, and `stableResources=1`. + +Signal/wait and barrier are implemented and covered by source-level tests. +They require their own hardware run before making a validation claim for a +new server or CANN/driver combination. + +## Failure Diagnostics + +On a timeout, the probe reports: + +- rank and loop index; +- submit and stream synchronization return codes; +- mission id, key, start/end/current instruction; +- local and remote XN values; +- local wait, remote notify, source, and completion CKE values; +- first and last data mismatch offsets and affected 32 KB blocks. + +All ranks stopping on the same wait instruction indicates a route, remote XN, +or CKE synchronization problem. A zero mismatch count before stream +completion does not prove success because destination validation has not run. + +## Remaining Limitations + +- AllToAll remains an internal/test-only direct CCU path. +- The mesh software contract reaches 64 ranks, but hardware validation in + this change covers four ranks. +- The smoke probe supports only selected payload sizes even though the + builders accept aligned sizes. +- Mesh loop reuse currently has no device generation marker. +- LoopEngine and LoopGroup are not implemented. +- Signal/wait and barrier remain two-rank only. +- Cleanup after timeout may be skipped by the smoke probe to isolate runtime + cleanup hangs; the outer timeout remains mandatory. diff --git a/src/collectives/CMakeLists.txt b/src/collectives/CMakeLists.txt index 258f29ef..b73bda17 100644 --- a/src/collectives/CMakeLists.txt +++ b/src/collectives/CMakeLists.txt @@ -23,12 +23,14 @@ add_custom_command( ) set(TILEXR_COLLECTIVES_SOURCE_FILE + host/collective_backend.cpp host/collective_launcher.cpp host/collective_utils.cpp host/collective_kernel.cpp host/perf_trace_report.cpp host/perf_trace_session.cpp host/tilexr_collectives.cpp + host/collective_backend.h ${TILEXR_COLLECTIVES_KERNEL_EMBED_CPP} ) diff --git a/src/collectives/host/collective_backend.cpp b/src/collectives/host/collective_backend.cpp new file mode 100644 index 00000000..0896a66a --- /dev/null +++ b/src/collectives/host/collective_backend.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024-2026 TileXR Project + * This file is a part of the CANN Open Software. + * Licensed under CANN Open Software License Agreement Version 1.0 (the "License"). + * Please refer to the License for details. You may not use this file except in compliance with the License. + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. + * See LICENSE in the root of the software repository for the full text of the License. + */ +#include "collective_backend.h" + +#include "../../comm/ccu/tilexr_ccu_backend.h" +#include "../../comm/tilexr_comm.h" + +namespace TileXRCollectives { +namespace Host { +namespace { + +BackendTestState g_testState {}; + +int DispatchAiv(const CollectiveRequest &request) +{ + (void)request; + return g_testState.enabled ? g_testState.aivReturn : TileXR::TILEXR_SUCCESS; +} + +int DispatchUdma(const CollectiveRequest &request) +{ + if (g_testState.enabled) { + if (!g_testState.udmaInitialized) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return g_testState.udmaSupported ? g_testState.udmaReturn : TileXR::TILEXR_ERROR_NOT_SUPPORT; + } + + auto *comm = static_cast(request.comm); + if (comm == nullptr || !comm->IsUdmaAvailableForCollectives()) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return TileXR::TILEXR_ERROR_NOT_SUPPORT; +} + +int DispatchCcu(const CollectiveRequest &request) +{ + if (g_testState.enabled) { + if (!g_testState.ccuInitialized) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + return g_testState.ccuSupported ? g_testState.ccuReturn : TileXR::TILEXR_ERROR_NOT_SUPPORT; + } + + auto *comm = static_cast(request.comm); + TileXR::TileXRCcuBackend *backend = comm->GetCcuBackendForCollectives(); + if (backend == nullptr || !backend->Available()) { + return TileXR::TILEXR_ERROR_NOT_INITIALIZED; + } + TileXR::TileXRCcuCollectiveRequest ccuRequest {}; + ccuRequest.type = request.type; + ccuRequest.sendBuf = request.sendBuf; + ccuRequest.recvBuf = request.recvBuf; + ccuRequest.count = request.count; + ccuRequest.dataType = request.dataType; + ccuRequest.reduceOp = request.reduceOp; + ccuRequest.root = request.root; + ccuRequest.stream = request.stream; + TileXR::TileXRCcuCollectivePlan plan {}; + const int ret = backend->PrepareCollective(ccuRequest, &plan); + if (ret != TileXR::TILEXR_SUCCESS) { + return ret; + } + return backend->SubmitCollective(plan, request.stream); +} + +} // namespace + +int DispatchCollective(const CollectiveRequest &request, TileXRCollectiveBackend backend) +{ + if (request.comm == nullptr || request.sendBuf == nullptr || request.recvBuf == nullptr || request.count <= 0) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + switch (backend) { + case TILEXR_COLLECTIVE_BACKEND_AIV: + return DispatchAiv(request); + case TILEXR_COLLECTIVE_BACKEND_UDMA: + return DispatchUdma(request); + case TILEXR_COLLECTIVE_BACKEND_CCU: + return DispatchCcu(request); + case TILEXR_COLLECTIVE_BACKEND_AUTO: + default: + if (g_testState.enabled && g_testState.ccuInitialized && g_testState.ccuSupported) { + return DispatchCcu(request); + } + if (g_testState.enabled && g_testState.udmaInitialized && g_testState.udmaSupported) { + return DispatchUdma(request); + } + return DispatchAiv(request); + } +} + +void SetBackendTestState(const BackendTestState &state) +{ + g_testState = state; + g_testState.enabled = true; +} + +void ResetBackendTestState() +{ + g_testState = BackendTestState {}; +} + +} // namespace Host +} // namespace TileXRCollectives diff --git a/src/collectives/host/collective_backend.h b/src/collectives/host/collective_backend.h new file mode 100644 index 00000000..cd897fe5 --- /dev/null +++ b/src/collectives/host/collective_backend.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2026 TileXR Project + * This file is a part of the CANN Open Software. + * Licensed under CANN Open Software License Agreement Version 1.0 (the "License"). + * Please refer to the License for details. You may not use this file except in compliance with the License. + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. + * See LICENSE in the root of the software repository for the full text of the License. + */ +#ifndef TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H +#define TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H + +#include + +#include "acl/acl_base.h" +#include "tilexr_collectives.h" +#include "tilexr_types.h" + +namespace TileXRCollectives { +namespace Host { + +struct CollectiveRequest { + TileXR::TileXRType type = TileXR::TileXRType::ALL_GATHER; + void *sendBuf = nullptr; + void *recvBuf = nullptr; + int64_t count = 0; + TileXR::TileXRDataType dataType = TileXR::TILEXR_DATA_TYPE_RESERVED; + TileXR::TileXRReduceOp reduceOp = TileXR::TILEXR_REDUCE_RESERVED; + int root = 0; + TileXRCommPtr comm = nullptr; + aclrtStream stream = nullptr; +}; + +struct BackendTestState { + bool enabled = false; + bool udmaInitialized = false; + bool udmaSupported = false; + int udmaReturn = TileXR::TILEXR_ERROR_NOT_SUPPORT; + bool ccuInitialized = false; + bool ccuSupported = false; + int ccuReturn = TileXR::TILEXR_ERROR_NOT_SUPPORT; + int aivReturn = TileXR::TILEXR_SUCCESS; +}; + +int DispatchCollective(const CollectiveRequest &request, TileXRCollectiveBackend backend); +void SetBackendTestState(const BackendTestState &state); +void ResetBackendTestState(); + +} // namespace Host +} // namespace TileXRCollectives + +#endif // TILEXR_COLLECTIVES_HOST_COLLECTIVE_BACKEND_H diff --git a/src/collectives/host/tilexr_collectives.cpp b/src/collectives/host/tilexr_collectives.cpp index d91ed87c..bf52a81a 100644 --- a/src/collectives/host/tilexr_collectives.cpp +++ b/src/collectives/host/tilexr_collectives.cpp @@ -10,6 +10,7 @@ #include #include "acl/acl_rt.h" +#include "collective_backend.h" #include "collective_kernel.h" #include "collective_launcher.h" #include "collective_utils.h" @@ -66,16 +67,38 @@ int LoopbackCopy(void *sendBuf, void *recvBuf, int64_t bytes, aclrtStream stream return ret == ACL_SUCCESS ? TileXR::TILEXR_SUCCESS : TileXR::TILEXR_ERROR_INTERNAL; } +TileXRCollectiveBackend SelectedBackend(const TileXRCollectiveOptions *options) +{ + return options == nullptr ? TILEXR_COLLECTIVE_BACKEND_AUTO : options->backend; +} + +bool UsesForcedNonAivBackend(TileXRCollectiveBackend backend) +{ + return backend == TILEXR_COLLECTIVE_BACKEND_UDMA || backend == TILEXR_COLLECTIVE_BACKEND_CCU; +} + } // namespace -int TileXRAllGather(void *sendBuf, void *recvBuf, int64_t sendCount, - TileXR::TileXRDataType dataType, TileXRCommPtr comm, - aclrtStream stream) +int TileXRAllGatherEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options) { int ret = ValidateCommon(sendBuf, recvBuf, sendCount, dataType, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL_GATHER; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = sendCount; + request.dataType = dataType; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -93,14 +116,33 @@ int TileXRAllGather(void *sendBuf, void *recvBuf, int64_t sendCount, sendBuf, recvBuf, sendCount, dataType, blockDim, stream); } -int TileXRAllToAll(void *sendBuf, void *recvBuf, int64_t sendCount, - TileXR::TileXRDataType dataType, TileXRCommPtr comm, - aclrtStream stream) +int TileXRAllGather(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream) +{ + return TileXRAllGatherEx(sendBuf, recvBuf, sendCount, dataType, comm, stream, nullptr); +} + +int TileXRAllToAllEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options) { int ret = ValidateCommon(sendBuf, recvBuf, sendCount, dataType, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL2ALL; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = sendCount; + request.dataType = dataType; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -133,14 +175,34 @@ int TileXRAllToAll(void *sendBuf, void *recvBuf, int64_t sendCount, sendBuf, recvBuf, kernelCount, dataType, blockDim, stream); } -int TileXRAllReduce(void *sendBuf, void *recvBuf, int64_t count, - TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, - TileXRCommPtr comm, aclrtStream stream) +int TileXRAllToAll(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream) +{ + return TileXRAllToAllEx(sendBuf, recvBuf, sendCount, dataType, comm, stream, nullptr); +} + +int TileXRAllReduceEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options) { int ret = ValidateReduce(sendBuf, recvBuf, count, dataType, op, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL_REDUCE; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = count; + request.dataType = dataType; + request.reduceOp = op; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -159,14 +221,35 @@ int TileXRAllReduce(void *sendBuf, void *recvBuf, int64_t count, TileXRCollectives::Host::CollectiveLaunchAttrs { static_cast(op), 0 }); } -int TileXRReduceScatter(void *sendBuf, void *recvBuf, int64_t recvCount, - TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, - TileXRCommPtr comm, aclrtStream stream) +int TileXRAllReduce(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream) +{ + return TileXRAllReduceEx(sendBuf, recvBuf, count, dataType, op, comm, stream, nullptr); +} + +int TileXRReduceScatterEx(void *sendBuf, void *recvBuf, int64_t recvCount, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, + const TileXRCollectiveOptions *options) { int ret = ValidateReduce(sendBuf, recvBuf, recvCount, dataType, op, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::REDUCE_SCATTER; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = recvCount; + request.dataType = dataType; + request.reduceOp = op; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -195,14 +278,34 @@ int TileXRReduceScatter(void *sendBuf, void *recvBuf, int64_t recvCount, TileXRCollectives::Host::CollectiveLaunchAttrs { static_cast(op), 0 }); } -int TileXRBroadcast(void *buf, int64_t count, - TileXR::TileXRDataType dataType, int root, - TileXRCommPtr comm, aclrtStream stream) +int TileXRReduceScatter(void *sendBuf, void *recvBuf, int64_t recvCount, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream) +{ + return TileXRReduceScatterEx(sendBuf, recvBuf, recvCount, dataType, op, comm, stream, nullptr); +} + +int TileXRBroadcastEx(void *buf, int64_t count, + TileXR::TileXRDataType dataType, int root, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options) { int ret = ValidateBroadcastLocal(buf, count, dataType, root, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::BROADCAST; + request.sendBuf = buf; + request.recvBuf = buf; + request.count = count; + request.dataType = dataType; + request.root = root; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -227,14 +330,33 @@ int TileXRBroadcast(void *buf, int64_t count, TileXRCollectives::Host::CollectiveLaunchAttrs { 0, root }); } -int TileXRProfileProbe(void *sendBuf, void *recvBuf, int64_t count, - TileXR::TileXRDataType dataType, TileXRCommPtr comm, - aclrtStream stream) +int TileXRBroadcast(void *buf, int64_t count, + TileXR::TileXRDataType dataType, int root, + TileXRCommPtr comm, aclrtStream stream) +{ + return TileXRBroadcastEx(buf, count, dataType, root, comm, stream, nullptr); +} + +int TileXRProfileProbeEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options) { int ret = ValidateCommon(sendBuf, recvBuf, count, dataType, comm); if (ret != TileXR::TILEXR_SUCCESS) { return ret; } + const TileXRCollectiveBackend backend = SelectedBackend(options); + if (UsesForcedNonAivBackend(backend)) { + TileXRCollectives::Host::CollectiveRequest request {}; + request.type = TileXR::TileXRType::PROFILE_PROBE; + request.sendBuf = sendBuf; + request.recvBuf = recvBuf; + request.count = count; + request.dataType = dataType; + request.comm = comm; + request.stream = stream; + return TileXRCollectives::Host::DispatchCollective(request, backend); + } TileXRCollectives::Host::HostLaunchContext context; ret = TileXRCollectives::Host::PrepareHostLaunchContext(comm, context); @@ -250,3 +372,10 @@ int TileXRProfileProbe(void *sendBuf, void *recvBuf, int64_t count, return TileXRCollectives::Host::LaunchCollectiveKernel(comm, TileXR::TileXRType::PROFILE_PROBE, context, sendBuf, recvBuf, bytes, TileXR::TILEXR_DATA_TYPE_INT8, blockDim, stream); } + +int TileXRProfileProbe(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream) +{ + return TileXRProfileProbeEx(sendBuf, recvBuf, count, dataType, comm, stream, nullptr); +} diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index e0fd5253..5c64771e 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -105,6 +105,58 @@ 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_alltoall_program.h + ccu/tilexr_ccu_alltoall_program.cpp + ccu/tilexr_ccu_barrier_program.h + ccu/tilexr_ccu_barrier_program.cpp + ccu/tilexr_ccu_backend.h + ccu/tilexr_ccu_backend.cpp + ccu/tilexr_ccu_collective_planner.h + ccu/tilexr_ccu_collective_planner.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_signal_wait_program.h + ccu/tilexr_ccu_signal_wait_program.cpp + ccu/tilexr_ccu_topology.h + ccu/tilexr_ccu_topology.cpp + ccu/tilexr_ccu_runtime_session.h + ccu/tilexr_ccu_runtime_session.cpp + ccu/tilexr_ccu_executor.h + ccu/tilexr_ccu_executor.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 @@ -121,6 +173,12 @@ 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) +target_compile_definitions(tile-comm PRIVATE TILEXR_CCU_TESTING=1) if(TILEXR_HAVE_PTO_SDMA) target_compile_definitions(tile-comm PRIVATE TILEXR_HAVE_PTO_SDMA=1) @@ -136,6 +194,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 00000000..10b1692d --- /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_alltoall_program.cpp b/src/comm/ccu/tilexr_ccu_alltoall_program.cpp new file mode 100644 index 00000000..e54981f0 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_alltoall_program.cpp @@ -0,0 +1,1161 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_alltoall_program.h" + +#include +#include + +namespace TileXR { +namespace { + +constexpr uint16_t TILEXR_CCU_TRACE_LOAD_SQE_ARGS_TO_X_HEADER = 0x0001U; +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_LOC_MEM_TO_RMT_MEM_HEADER = 0x1009U; +constexpr uint16_t TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_LOC_MEM_HEADER = 0x100aU; +constexpr uint16_t TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_LOC_MS_HEADER = 0x1000U; +constexpr uint16_t TILEXR_CCU_TRACE_TRANS_LOC_MS_TO_LOC_MEM_HEADER = 0x1002U; +constexpr uint32_t TILEXR_CCU_ALLTOALL_MAX_RANK_SIZE = 64U; +constexpr uint32_t TILEXR_CCU_CKE_MASK_BITS = 16U; + +uint32_t CompletionCkeCount(size_t peerCount) +{ + return static_cast((peerCount + TILEXR_CCU_CKE_MASK_BITS - 1U) / TILEXR_CCU_CKE_MASK_BITS); +} + +uint16_t CompletionMaskForGroup(size_t peerCount, uint32_t group) +{ + const size_t begin = static_cast(group) * TILEXR_CCU_CKE_MASK_BITS; + const size_t remaining = peerCount > begin ? peerCount - begin : 0U; + const uint32_t bits = static_cast(std::min(remaining, TILEXR_CCU_CKE_MASK_BITS)); + return bits == TILEXR_CCU_CKE_MASK_BITS ? 0xffffU : static_cast((1U << bits) - 1U); +} + +size_t MeshPreSyncInstructionCount(size_t peerCount) +{ + return 3U + peerCount * 3U; +} + +size_t MeshCopyInstructionCountPerBlock(size_t peerCount) +{ + return peerCount * 6U + 9U + CompletionCkeCount(peerCount); +} + +constexpr uint16_t TILEXR_CCU_TRACE_SYNC_CKE_HEADER = 0x100bU; +constexpr uint16_t TILEXR_CCU_TRACE_SYNC_XN_HEADER = 0x100dU; +constexpr uint16_t TILEXR_CCU_ALLTOALL_SOURCE_CKE_INIT_MASK = 0xffffU; + +uint16_t PreSyncSignalMask(const TileXRCcuAllToAll2RankProgramSpec& spec) +{ + (void)spec; + return static_cast(1U << TILEXR_CCU_ALLTOALL_OUTPUT_XN_ID); +} + +uint16_t PreSyncTokenMask(const TileXRCcuAllToAll2RankProgramSpec& spec) +{ + (void)spec; + return static_cast(1U << TILEXR_CCU_ALLTOALL_TOKEN_XN_ID); +} + +uint16_t PostSyncSignalMask(const TileXRCcuAllToAll2RankProgramSpec& spec) +{ + return spec.ckeMask; +} + +void ResetReport(TileXRCcuAllToAllProgramReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuAllToAllProgramReport{}; + } +} + +int Fail( + std::vector* program, + TileXRCcuAllToAllProgramReport* 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 TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + if (program == nullptr) { + return Fail(program, report, "missing output direct CCU alltoall program"); + } + if (spec.localRank > 1U) { + return Fail(program, report, "direct CCU alltoall localRank must be 0 or 1"); + } + if (spec.localSendAddr == 0 || spec.localRecvAddr == 0 || spec.remoteRecvAddr == 0) { + return Fail(program, report, "missing direct CCU alltoall address"); + } + if (spec.localSendToken == 0 || spec.localRecvToken == 0 || spec.remoteRecvToken == 0) { + return Fail(program, report, "missing direct CCU alltoall token"); + } + if (spec.bytes == 0 || spec.bytes % TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES != 0) { + return Fail(program, report, "direct CCU alltoall bytes must be nonzero and 4KB aligned"); + } + if (spec.memorySliceBytes != TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES) { + return Fail(program, report, "direct CCU alltoall memorySliceBytes must be 4096"); + } + if (spec.memSlicePerBlock == 0 || spec.memSlicePerBlock > TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK) { + return Fail(program, report, "direct CCU alltoall memSlicePerBlock must be in [1, 8]"); + } + const uint64_t bytesPerBlock = static_cast(spec.memorySliceBytes) * spec.memSlicePerBlock; + if (spec.bytes % bytesPerBlock != 0) { + return Fail(program, report, "direct CCU alltoall bytes must align to memSlicePerBlock"); + } + if (spec.localGsa == 0 || spec.remoteGsa == 0 || spec.localXn == 0 || spec.remoteXn == 0 || + spec.lengthXn == 0) { + return Fail(program, report, "missing direct CCU alltoall GSA/XN resource"); + } + const uint16_t preSyncChannelId = spec.preSyncChannelId == 0 ? spec.channelId : spec.preSyncChannelId; + const uint16_t copyChannelId = spec.copyChannelId == 0 ? spec.channelId : spec.copyChannelId; + const uint16_t postSyncChannelId = spec.postSyncChannelId == 0 ? spec.channelId : spec.postSyncChannelId; + if (preSyncChannelId == 0 || copyChannelId == 0 || postSyncChannelId == 0 || + spec.copyCompletionCke == 0 || spec.preSyncLocalWaitCke == 0 || + spec.preSyncRemoteNotifyCke == 0 || spec.postSyncLocalWaitCke == 0 || + spec.postSyncRemoteNotifyCke == 0 || + (spec.postSyncNotify && spec.sourceCke == 0) || spec.ckeMask == 0) { + return Fail(program, report, "missing direct CCU alltoall CKE/channel resource"); + } + if (spec.preSyncMarkerEnabled && + (spec.preSyncLocalMarkerXn == 0 || spec.preSyncRemoteMarkerXn == 0 || + spec.preSyncMarkerArgIndex >= TILEXR_CCU_SQE_ARGS_LEN)) { + return Fail(program, report, "missing direct CCU alltoall loop marker resource"); + } + return TILEXR_SUCCESS; +} + +int AppendSetSourceCke( + const TileXRCcuAllToAll2RankProgramSpec& spec, + uint16_t mask, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuCkeSpec set; + set.ckeId = spec.sourceCke; + set.mask = mask; + set.clearWait = true; + + TileXRCcuInstr instr; + if (TileXRCcuEncodeSetCke(set, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall source CKE set"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendNotifyWait( + uint16_t localWaitCke, + uint16_t mask, + const char* phase, + bool clearCkeWait, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuCkeSpec wait; + wait.waitCkeId = localWaitCke; + wait.waitMask = mask; + wait.clearWait = true; + + TileXRCcuInstr instr; + const int ret = clearCkeWait ? + TileXRCcuEncodeClearCke(wait, &instr) : + TileXRCcuEncodeSetCke(wait, &instr); + if (ret != TILEXR_SUCCESS) { + return Fail(program, report, std::string("failed to encode direct CCU alltoall ") + phase + " NotifyWait"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendSyncPair( + uint16_t remoteNotifyCke, + uint16_t localWaitCke, + uint16_t channelId, + const TileXRCcuAllToAll2RankProgramSpec& spec, + uint16_t localMask, + uint16_t waitMask, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuSyncCkeSpec post; + post.remoteCke = remoteNotifyCke; + post.localCke = spec.sourceCke; + post.localCkeMask = localMask; + post.channelId = channelId; + post.clearWait = true; + + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall SyncCke"); + } + program->push_back(instr); + + return AppendNotifyWait(localWaitCke, waitMask, "PostSync", true, program, report); +} + +int AppendRemoteNotify( + uint16_t remoteNotifyCke, + uint16_t channelId, + uint16_t localXn, + uint16_t remoteXn, + uint64_t value, + uint16_t secFlag, + uint16_t mask, + const char* phase, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(localXn, value, secFlag, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, std::string("failed to encode direct CCU alltoall ") + phase + " variable load"); + } + program->push_back(instr); + + TileXRCcuSyncXnSpec notify; + notify.remoteXn = remoteXn; + notify.localXn = localXn; + notify.channelId = channelId; + notify.notifyCke = remoteNotifyCke; + notify.notifyMask = mask; + notify.clearWait = true; + + if (TileXRCcuEncodeSyncXn(notify, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, std::string("failed to encode direct CCU alltoall ") + phase + " SyncXn notify"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendSyncXnNotify( + uint16_t remoteNotifyCke, + uint16_t channelId, + uint16_t localXn, + uint16_t remoteXn, + uint16_t mask, + const char* phase, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuSyncXnSpec notify; + notify.remoteXn = remoteXn; + notify.localXn = localXn; + notify.channelId = channelId; + notify.notifyCke = remoteNotifyCke; + notify.notifyMask = mask; + notify.clearWait = true; + + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncXn(notify, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, std::string("failed to encode direct CCU alltoall ") + phase + + " SyncXn notify"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendRemoteMarkerNotify( + uint16_t remoteNotifyCke, + uint16_t channelId, + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadSqeArgsToX( + spec.preSyncLocalMarkerXn, + spec.preSyncMarkerArgIndex, + &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall PreSync loop marker load"); + } + program->push_back(instr); + + TileXRCcuSyncXnSpec notify; + notify.remoteXn = spec.preSyncRemoteMarkerXn; + notify.localXn = spec.preSyncLocalMarkerXn; + notify.channelId = channelId; + notify.notifyCke = remoteNotifyCke; + notify.notifyMask = TILEXR_CCU_ALLTOALL_LOOP_MARKER_MASK; + notify.clearWait = true; + if (TileXRCcuEncodeSyncXn(notify, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall PreSync loop marker notify"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendPreSyncPhase( + uint16_t remoteNotifyCke, + uint16_t localWaitCke, + uint16_t outputChannelId, + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + const uint16_t outputMask = PreSyncSignalMask(spec); + const uint16_t tokenMask = PreSyncTokenMask(spec); + const uint16_t markerMask = spec.preSyncMarkerEnabled ? TILEXR_CCU_ALLTOALL_LOOP_MARKER_MASK : 0U; + const uint16_t waitMask = static_cast(markerMask | outputMask | tokenMask); + const uint16_t localOutputXn = + spec.preSyncLocalAddrXn == 0 ? spec.localXn : spec.preSyncLocalAddrXn; + const uint16_t localTokenXn = + spec.preSyncLocalTokenXn == 0 ? spec.lengthXn : spec.preSyncLocalTokenXn; + const uint16_t tokenChannelId = + spec.preSyncTokenChannelId == 0 ? outputChannelId : spec.preSyncTokenChannelId; + const uint16_t markerChannelId = + spec.preSyncMarkerChannelId == 0 ? outputChannelId : spec.preSyncMarkerChannelId; + const uint16_t tokenNotifyCke = + spec.preSyncRemoteTokenNotifyCke == 0 ? remoteNotifyCke : spec.preSyncRemoteTokenNotifyCke; + if (spec.preSyncMarkerEnabled && + AppendRemoteMarkerNotify( + remoteNotifyCke, + markerChannelId, + spec, + program, + report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AppendRemoteNotify( + remoteNotifyCke, + outputChannelId, + localOutputXn, + spec.preSyncRemoteAddrXn, + spec.localRecvAddr, + 0, + outputMask, + "PreSync output", + program, + report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AppendRemoteNotify( + tokenNotifyCke, + tokenChannelId, + localTokenXn, + spec.preSyncRemoteTokenXn, + spec.localRecvToken, + 1, + tokenMask, + "PreSync token", + program, + report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + if (!spec.preSyncWait) { + return TILEXR_SUCCESS; + } + + return AppendNotifyWait(localWaitCke, waitMask, "PreSync output", false, program, report); +} + +int AppendPostSyncPhase( + uint16_t remoteNotifyCke, + uint16_t localWaitCke, + uint16_t channelId, + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + const uint16_t notifyMask = PostSyncSignalMask(spec); + const uint16_t waitMask = PostSyncSignalMask(spec); + if (AppendSetSourceCke(spec, notifyMask, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!spec.postSyncWait) { + TileXRCcuSyncCkeSpec post; + post.remoteCke = remoteNotifyCke; + post.localCke = spec.sourceCke; + post.localCkeMask = notifyMask; + post.channelId = channelId; + post.clearWait = true; + + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall PostSync notify-only SyncCke"); + } + program->push_back(instr); + return TILEXR_SUCCESS; + } + return AppendSyncPair( + remoteNotifyCke, + localWaitCke, + channelId, + spec, + notifyMask, + waitMask, + program, + report); +} + +int AppendCopyBlock( + const TileXRCcuAllToAll2RankProgramSpec& spec, + uint64_t offset, + uint64_t bytesPerBlock, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuMemoryCopySpec copy; + copy.direction = TileXRCcuMemoryCopyDirection::LocalToRemote; + copy.localGsa = spec.localGsa; + copy.localXn = spec.localXn; + copy.remoteGsa = spec.remoteGsa; + copy.remoteXn = spec.remoteXn; + copy.lengthXn = spec.lengthXn; + copy.localAddr = spec.localSendAddr + offset; + copy.localToken = spec.localSendToken; + copy.remoteAddr = spec.remoteRecvAddr + offset; + copy.remoteToken = spec.remoteRecvToken; + copy.lengthBytes = bytesPerBlock; + copy.channelId = spec.copyChannelId == 0 ? spec.channelId : spec.copyChannelId; + copy.completionCke = spec.copyCompletionCke; + copy.completionMask = spec.ckeMask; + + std::vector block; + TileXRCcuMemoryProgramReport memoryReport; + if (TileXRCcuBuildMemoryCopyProgram(copy, &block, &memoryReport) != TILEXR_SUCCESS) { + return Fail(program, report, memoryReport.message); + } + program->insert(program->end(), block.begin(), block.end()); + return TILEXR_SUCCESS; +} + +int AppendMeshRemoteCopyBlock( + const TileXRCcuAllToAll2RankProgramSpec& spec, + uint64_t offset, + uint64_t bytesPerBlock, + uint16_t completionCke, + uint16_t completionMask, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuMemoryCopySpec copy; + copy.direction = TileXRCcuMemoryCopyDirection::LocalToRemote; + copy.localGsa = spec.localGsa; + copy.localXn = spec.localXn; + copy.remoteGsa = spec.remoteGsa; + copy.remoteXn = spec.remoteXn; + copy.lengthXn = spec.lengthXn; + copy.localAddr = spec.localSendAddr + offset; + copy.localToken = spec.localSendToken; + copy.remoteAddr = spec.remoteRecvAddr + offset; + copy.remoteToken = spec.remoteRecvToken; + copy.lengthBytes = bytesPerBlock; + copy.channelId = spec.copyChannelId == 0 ? spec.channelId : spec.copyChannelId; + copy.completionCke = completionCke; + copy.completionMask = completionMask; + + std::vector block; + TileXRCcuMemoryProgramReport memoryReport; + if (TileXRCcuBuildMemoryCopyProgram(copy, &block, &memoryReport) != TILEXR_SUCCESS || block.size() != 7U) { + return Fail(program, report, memoryReport.message.empty() ? + "failed to build direct CCU alltoall mesh remote copy block" : memoryReport.message); + } + block.pop_back(); + program->insert(program->end(), block.begin(), block.end()); + return TILEXR_SUCCESS; +} + +int AppendFinish( + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(spec.localXn, 0, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall finish instruction"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +void FillReport( + const TileXRCcuAllToAll2RankProgramSpec& spec, + const std::vector& program, + TileXRCcuAllToAllProgramReport* report) +{ + if (report == nullptr) { + return; + } + const uint32_t bytesPerBlock = spec.memorySliceBytes * spec.memSlicePerBlock; + const uint32_t markerInstructionCount = spec.preSyncMarkerEnabled ? 2U : 0U; + report->preSyncInstructionCount = + spec.preSyncNotify ? (spec.preSyncWait ? 5U : 4U) + markerInstructionCount : 0U; + report->blockCount = static_cast(spec.bytes / bytesPerBlock); + report->bytesPerBlock = bytesPerBlock; + report->copyInstructionCount = report->blockCount * 7U; + report->postSyncInstructionCount = !spec.postSyncNotify ? 0U : (spec.postSyncWait ? 3U : 2U); + report->finishInstructionCount = spec.emitFinish ? 1U : 0U; + report->totalInstructionCount = static_cast(program.size()); + report->message = "ok"; +} + +int ValidateMeshSpec( + const TileXRCcuAllToAllMeshProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + if (program == nullptr) { + return Fail(program, report, "missing output direct CCU alltoall mesh program"); + } + if (spec.rankSize < 2U || spec.rankSize > TILEXR_CCU_ALLTOALL_MAX_RANK_SIZE || + spec.localRank >= spec.rankSize || spec.peers.size() != spec.rankSize - 1U) { + return Fail(program, report, "direct CCU alltoall mesh requires 2..64 ranks and rankSize-1 peers"); + } + if (spec.localSendAddr == 0 || spec.localRecvAddr == 0 || + spec.localSendToken == 0 || spec.localRecvToken == 0 || spec.chunkBytes == 0 || + spec.chunkBytes % TILEXR_CCU_ALLTOALL_BLOCK_BYTES != 0) { + return Fail(program, report, "invalid direct CCU alltoall mesh local buffer"); + } + if (spec.selfSourceGsa == 0 || spec.selfDestinationGsa == 0 || spec.selfSourceXn == 0 || + spec.selfDestinationXn == 0 || spec.selfLengthXn == 0 || + spec.selfCompletionCke == 0 || + spec.remoteCompletionCkes.size() != CompletionCkeCount(spec.peers.size())) { + return Fail(program, report, "missing direct CCU alltoall mesh self-copy resource"); + } + std::vector peerRanks(spec.rankSize, false); + std::set channelIds; + std::set completionCkes; + const auto& sharedRoute = spec.peers.front().route; + if (spec.selfSourceXn != sharedRoute.localXn || + spec.selfDestinationXn != sharedRoute.preSyncLocalTokenXn || + spec.selfLengthXn != sharedRoute.lengthXn) { + return Fail(program, report, "alltoall mesh self copy must share source, destination, and length XNs"); + } + for (uint16_t completionCke : spec.remoteCompletionCkes) { + if (completionCke == 0 || completionCke == sharedRoute.sourceCke || + !completionCkes.insert(completionCke).second) { + return Fail(program, report, + "alltoall mesh completion CKE overlaps source CKE or duplicates another completion CKE"); + } + } + for (size_t ordinal = 0; ordinal < spec.peers.size(); ++ordinal) { + const auto& peer = spec.peers[ordinal]; + if (peer.peerRank >= spec.rankSize || peer.peerRank == spec.localRank || peerRanks[peer.peerRank]) { + return Fail(program, report, "invalid direct CCU alltoall mesh peer rank"); + } + peerRanks[peer.peerRank] = true; + if (peer.route.localRank != spec.localRank || peer.route.localSendAddr != spec.localSendAddr || + peer.route.localSendToken != spec.localSendToken || peer.route.localRecvAddr != spec.localRecvAddr || + peer.route.localRecvToken != spec.localRecvToken || peer.route.bytes != spec.chunkBytes || + peer.route.preSyncMarkerEnabled || !peer.route.preSyncNotify || !peer.route.preSyncWait || + !peer.route.postSyncNotify || !peer.route.postSyncWait) { + return Fail(program, report, "invalid direct CCU alltoall mesh peer route"); + } + if (peer.route.preSyncLocalAddrXn != sharedRoute.preSyncLocalAddrXn || + peer.route.preSyncLocalTokenXn != sharedRoute.preSyncLocalTokenXn || + peer.route.preSyncChannelId != peer.route.copyChannelId || + peer.route.preSyncTokenChannelId != peer.route.copyChannelId || + peer.route.postSyncChannelId != peer.route.copyChannelId || + peer.route.copyCompletionCke != spec.remoteCompletionCkes[ordinal / TILEXR_CCU_CKE_MASK_BITS] || + peer.route.ckeMask != TILEXR_CCU_ALLTOALL_POST_SYNC_MASK || + !channelIds.insert(peer.route.copyChannelId).second) { + return Fail(program, report, "duplicate direct CCU alltoall mesh peer resource"); + } + TileXRCcuAllToAll2RankProgramSpec validationRoute = peer.route; + validationRoute.localRank = 0; + std::vector ignored; + TileXRCcuAllToAllProgramReport ignoredReport; + if (ValidateSpec(validationRoute, &ignored, &ignoredReport) != TILEXR_SUCCESS) { + return Fail(program, report, ignoredReport.message); + } + } + return TILEXR_SUCCESS; +} + +int AppendMeshPeerPosts( + const std::vector& peers, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + if (peers.empty()) { + return Fail(program, report, "missing direct CCU alltoall mesh peers"); + } + const auto& shared = peers.front().route; + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(shared.preSyncLocalAddrXn, shared.localRecvAddr, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh output variable"); + } + program->push_back(instr); + if (TileXRCcuEncodeLoadImdToXn(shared.preSyncLocalTokenXn, shared.localRecvToken, 1, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh token variable"); + } + program->push_back(instr); + + if (AppendSetSourceCke(shared, TILEXR_CCU_ALLTOALL_SOURCE_CKE_INIT_MASK, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + for (const auto& peer : peers) { + const auto& route = peer.route; + if (AppendSyncXnNotify( + route.preSyncRemoteNotifyCke, + route.preSyncChannelId, + shared.preSyncLocalAddrXn, + route.preSyncRemoteAddrXn, + PreSyncSignalMask(route), + "mesh PreSync output", + program, + report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AppendSyncXnNotify( + route.preSyncRemoteTokenNotifyCke, + route.preSyncTokenChannelId, + shared.preSyncLocalTokenXn, + route.preSyncRemoteTokenXn, + PreSyncTokenMask(route), + "mesh PreSync token", + program, + report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + return TILEXR_SUCCESS; +} + +int AppendLocalCopyBlock( + const TileXRCcuAllToAllMeshProgramSpec& spec, + uint64_t offset, + uint64_t bytesPerBlock, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToGsa(spec.selfSourceGsa, spec.localSendAddr + offset, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh self source address"); + } + program->push_back(instr); + if (TileXRCcuEncodeLoadImdToXn(spec.selfSourceXn, spec.localSendToken, 1U, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh self source token"); + } + program->push_back(instr); + if (TileXRCcuEncodeLoadImdToGsa(spec.selfDestinationGsa, spec.localRecvAddr + offset, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh self destination address"); + } + program->push_back(instr); + if (TileXRCcuEncodeLoadImdToXn(spec.selfDestinationXn, spec.localRecvToken, 1U, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh self destination token"); + } + program->push_back(instr); + if (TileXRCcuEncodeLoadImdToXn(spec.selfLengthXn, bytesPerBlock, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to load direct CCU alltoall mesh self length"); + } + program->push_back(instr); + + TileXRCcuLocalMsTransferSpec transfer; + transfer.localGsa = spec.selfSourceGsa; + transfer.localXn = spec.selfSourceXn; + transfer.localMs = 0; + transfer.lengthXn = spec.selfLengthXn; + transfer.channelId = 0; + transfer.setCkeId = spec.selfCompletionCke; + transfer.setCkeMask = 1U; + if (TileXRCcuEncodeTransLocMemToLocMs(transfer, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall mesh self transfer to local MS"); + } + program->push_back(instr); + + TileXRCcuCkeSpec wait; + wait.waitCkeId = spec.selfCompletionCke; + wait.waitMask = 1U; + if (TileXRCcuEncodeClearCke(wait, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to wait direct CCU alltoall mesh self transfer"); + } + program->push_back(instr); + + transfer.localGsa = spec.selfDestinationGsa; + transfer.localXn = spec.selfDestinationXn; + if (TileXRCcuEncodeTransLocMsToLocMem(transfer, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall mesh self transfer from local MS"); + } + program->push_back(instr); + if (TileXRCcuEncodeClearCke(wait, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to wait direct CCU alltoall mesh self transfer from local MS"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendMeshPostNotify( + const TileXRCcuAllToAll2RankProgramSpec& route, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + TileXRCcuSyncCkeSpec post; + post.remoteCke = route.postSyncRemoteNotifyCke; + post.localCke = route.sourceCke; + post.localCkeMask = route.ckeMask; + post.channelId = route.postSyncChannelId; + post.clearWait = true; + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to notify direct CCU alltoall mesh completion"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +uint16_t InstructionSlot(const TileXRCcuInstr& instr, uint32_t slot) +{ + return static_cast( + (instr.words[slot / 4U] >> ((slot % 4U) * 16U)) & 0xffffU); +} + +int FailBindingValidation(TileXRCcuAllToAllProgramReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = "direct CCU alltoall encoded binding validation failed: " + message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +bool MatchesSyncXn( + const TileXRCcuInstr& instr, + uint16_t remoteXn, + uint16_t localXn, + uint16_t channelId, + uint16_t notifyCke, + uint16_t notifyMask) +{ + return InstructionSlot(instr, 0) == TILEXR_CCU_TRACE_SYNC_XN_HEADER && + InstructionSlot(instr, 1) == remoteXn && + InstructionSlot(instr, 2) == localXn && + InstructionSlot(instr, 4) == channelId && + InstructionSlot(instr, 5) == notifyCke && + InstructionSlot(instr, 6) == notifyMask; +} + +bool MatchesWait( + const TileXRCcuInstr& instr, + uint16_t header, + uint16_t waitCke, + uint16_t waitMask) +{ + return InstructionSlot(instr, 0) == header && + InstructionSlot(instr, 4) == waitCke && + InstructionSlot(instr, 5) == waitMask; +} + +bool MatchesTransfer( + const TileXRCcuInstr& instr, + uint16_t header, + uint16_t remoteGsa, + uint16_t remoteXn, + uint16_t localGsa, + uint16_t localXn, + uint16_t lengthXn, + uint16_t channelId, + uint16_t completionCke, + uint16_t completionMask) +{ + return InstructionSlot(instr, 0) == header && + InstructionSlot(instr, 1) == remoteGsa && + InstructionSlot(instr, 2) == remoteXn && + InstructionSlot(instr, 3) == localGsa && + InstructionSlot(instr, 4) == localXn && + InstructionSlot(instr, 5) == lengthXn && + InstructionSlot(instr, 6) == channelId && + InstructionSlot(instr, 12) == completionCke && + InstructionSlot(instr, 13) == completionMask; +} + +bool MatchesLocalMsTransfer( + const TileXRCcuInstr& instr, + uint16_t header, + uint16_t localGsa, + uint16_t localXn, + uint16_t localMs, + uint16_t lengthXn, + uint16_t channelId, + uint16_t completionCke, + uint16_t completionMask) +{ + const bool memToMs = header == TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_LOC_MS_HEADER; + return InstructionSlot(instr, 0) == header && + InstructionSlot(instr, memToMs ? 1U : 3U) == localMs && + InstructionSlot(instr, memToMs ? 2U : 1U) == localGsa && + InstructionSlot(instr, memToMs ? 3U : 2U) == localXn && + InstructionSlot(instr, 4) == lengthXn && + InstructionSlot(instr, 5) == channelId && + InstructionSlot(instr, 12) == completionCke && + InstructionSlot(instr, 13) == completionMask; +} + +int ValidateMeshProgramBindings( + const TileXRCcuAllToAllMeshProgramSpec& spec, + const std::vector& program, + TileXRCcuAllToAllProgramReport* report) +{ + auto peers = spec.peers; + std::sort(peers.begin(), peers.end(), [](const TileXRCcuAllToAllMeshPeerSpec& lhs, + const TileXRCcuAllToAllMeshPeerSpec& rhs) { + return lhs.peerRank < rhs.peerRank; + }); + const uint32_t blocksPerChunk = static_cast( + spec.chunkBytes / TILEXR_CCU_ALLTOALL_BLOCK_BYTES); + const size_t preSyncInstructions = MeshPreSyncInstructionCount(peers.size()); + const size_t copyInstructionsPerBlock = MeshCopyInstructionCountPerBlock(peers.size()); + const size_t expectedSize = preSyncInstructions + + static_cast(blocksPerChunk) * copyInstructionsPerBlock + peers.size() * 2U + 1U; + if (peers.size() != spec.rankSize - 1U || blocksPerChunk == 0 || program.size() != expectedSize) { + return FailBindingValidation(report, "unexpected mesh program shape"); + } + + const auto& sharedRoute = peers.front().route; + if (InstructionSlot(program[0], 0) != TILEXR_CCU_TRACE_LOAD_IMD_TO_XN_HEADER || + InstructionSlot(program[0], 1) != sharedRoute.preSyncLocalAddrXn || + InstructionSlot(program[1], 0) != TILEXR_CCU_TRACE_LOAD_IMD_TO_XN_HEADER || + InstructionSlot(program[1], 1) != sharedRoute.preSyncLocalTokenXn || + InstructionSlot(program[2], 0) != TILEXR_CCU_TRACE_SET_CKE_HEADER || + InstructionSlot(program[2], 2) != sharedRoute.sourceCke || + InstructionSlot(program[2], 3) != TILEXR_CCU_ALLTOALL_SOURCE_CKE_INIT_MASK) { + return FailBindingValidation(report, "pre-sync variable loads do not match the shared mesh resources"); + } + for (size_t ordinal = 0; ordinal < peers.size(); ++ordinal) { + const auto& route = peers[ordinal].route; + if (!MatchesSyncXn( + program[3U + ordinal * 2U], + route.preSyncRemoteAddrXn, + sharedRoute.preSyncLocalAddrXn, + route.preSyncChannelId, + route.preSyncRemoteNotifyCke, + PreSyncSignalMask(route))) { + return FailBindingValidation(report, "output SyncXn does not match its peer route"); + } + if (!MatchesSyncXn( + program[4U + ordinal * 2U], + route.preSyncRemoteTokenXn, + sharedRoute.preSyncLocalTokenXn, + route.preSyncTokenChannelId, + route.preSyncRemoteTokenNotifyCke, + PreSyncTokenMask(route))) { + return FailBindingValidation(report, "token SyncXn does not match its peer route"); + } + const size_t wait = 3U + peers.size() * 2U + ordinal; + if (!MatchesWait( + program[wait], + TILEXR_CCU_TRACE_SET_CKE_HEADER, + route.preSyncLocalWaitCke, + static_cast(PreSyncSignalMask(route) | PreSyncTokenMask(route)))) { + return FailBindingValidation(report, "pre-sync waits do not match their peer route"); + } + } + + size_t instruction = preSyncInstructions; + for (uint32_t block = 0; block < blocksPerChunk; ++block) { + for (size_t ordinal = 0; ordinal < peers.size(); ++ordinal) { + const auto& route = peers[ordinal].route; + if (!MatchesTransfer( + program[instruction + 5U], + TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_RMT_MEM_HEADER, + route.remoteGsa, + route.remoteXn, + route.localGsa, + route.localXn, + route.lengthXn, + route.copyChannelId, + route.copyCompletionCke, + static_cast(1U << (ordinal % TILEXR_CCU_CKE_MASK_BITS)))) { + return FailBindingValidation(report, "remote copy does not match its peer route"); + } + instruction += 6U; + } + if (!MatchesLocalMsTransfer( + program[instruction + 5U], + TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_LOC_MS_HEADER, + spec.selfSourceGsa, + spec.selfSourceXn, + 0, + spec.selfLengthXn, + 0, + spec.selfCompletionCke, + 1U) || + !MatchesWait( + program[instruction + 6U], + TILEXR_CCU_TRACE_CLEAR_CKE_HEADER, + spec.selfCompletionCke, + 1U) || + !MatchesLocalMsTransfer( + program[instruction + 7U], + TILEXR_CCU_TRACE_TRANS_LOC_MS_TO_LOC_MEM_HEADER, + spec.selfDestinationGsa, + spec.selfDestinationXn, + 0, + spec.selfLengthXn, + 0, + spec.selfCompletionCke, + 1U) || + !MatchesWait( + program[instruction + 8U], + TILEXR_CCU_TRACE_CLEAR_CKE_HEADER, + spec.selfCompletionCke, + 1U)) { + return FailBindingValidation(report, "self copy does not match its local route"); + } + instruction += 9U; + for (uint32_t group = 0; group < spec.remoteCompletionCkes.size(); ++group) { + if (!MatchesWait( + program[instruction], + TILEXR_CCU_TRACE_CLEAR_CKE_HEADER, + spec.remoteCompletionCkes[group], + CompletionMaskForGroup(peers.size(), group))) { + return FailBindingValidation(report, "grouped remote copy wait does not match the mesh completion CKE"); + } + ++instruction; + } + } + for (const auto& peer : peers) { + const auto& route = peer.route; + if (InstructionSlot(program[instruction], 0) != TILEXR_CCU_TRACE_SYNC_CKE_HEADER || + InstructionSlot(program[instruction], 1) != route.postSyncRemoteNotifyCke || + InstructionSlot(program[instruction], 2) != route.sourceCke || + InstructionSlot(program[instruction], 3) != route.ckeMask || + InstructionSlot(program[instruction], 4) != route.postSyncChannelId) { + return FailBindingValidation(report, "post-sync notify does not match its peer route"); + } + ++instruction; + } + for (const auto& peer : peers) { + if (!MatchesWait( + program[instruction], + TILEXR_CCU_TRACE_CLEAR_CKE_HEADER, + peer.route.postSyncLocalWaitCke, + peer.route.ckeMask)) { + return FailBindingValidation(report, "post-sync wait does not match its peer route"); + } + ++instruction; + } + if (instruction + 1U != program.size() || + InstructionSlot(program[instruction], 0) != TILEXR_CCU_TRACE_LOAD_IMD_TO_XN_HEADER || + InstructionSlot(program[instruction], 1) != spec.selfSourceXn) { + return FailBindingValidation(report, "finish instruction does not match the mesh program"); + } + return TILEXR_SUCCESS; +} + +} // namespace + +int TileXRCcuValidateAllToAllMeshProgramBindings( + const TileXRCcuAllToAllMeshProgramSpec& spec, + const std::vector& program, + TileXRCcuAllToAllProgramReport* report) +{ + return ValidateMeshProgramBindings(spec, program, report); +} + +int TileXRCcuBuildAllToAll2RankProgram( + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + ResetReport(report); + if (program != nullptr) { + program->clear(); + } + int ret = ValidateSpec(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + const uint64_t bytesPerBlock = static_cast(spec.memorySliceBytes) * spec.memSlicePerBlock; + const uint32_t blockCount = static_cast(spec.bytes / bytesPerBlock); + const uint16_t preSyncChannelId = spec.preSyncChannelId == 0 ? spec.channelId : spec.preSyncChannelId; + const uint16_t postSyncChannelId = spec.postSyncChannelId == 0 ? spec.channelId : spec.postSyncChannelId; + const uint32_t markerInstructionCount = spec.preSyncMarkerEnabled ? 2U : 0U; + program->reserve( + (spec.preSyncNotify ? (spec.preSyncWait ? 5U : 4U) + markerInstructionCount : 0U) + blockCount * 7U + + (!spec.postSyncNotify ? 0U : (spec.postSyncWait ? 3U : 2U)) + + (spec.emitFinish ? 1U : 0U)); + + if (spec.preSyncNotify) { + ret = AppendPreSyncPhase( + spec.preSyncRemoteNotifyCke, + spec.preSyncLocalWaitCke, + preSyncChannelId, + spec, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + for (uint32_t block = 0; block < blockCount; ++block) { + const uint64_t offset = static_cast(block) * bytesPerBlock; + ret = AppendCopyBlock(spec, offset, bytesPerBlock, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + if (spec.postSyncNotify) { + ret = AppendPostSyncPhase( + spec.postSyncRemoteNotifyCke, + spec.postSyncLocalWaitCke, + postSyncChannelId, + spec, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + if (spec.emitFinish) { + ret = AppendFinish(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + FillReport(spec, *program, report); + return TILEXR_SUCCESS; +} + +int TileXRCcuBuildAllToAllMeshProgram( + const TileXRCcuAllToAllMeshProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report) +{ + ResetReport(report); + if (program != nullptr) { + program->clear(); + } + int ret = ValidateMeshSpec(spec, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + auto peers = spec.peers; + std::sort(peers.begin(), peers.end(), [](const TileXRCcuAllToAllMeshPeerSpec& lhs, + const TileXRCcuAllToAllMeshPeerSpec& rhs) { + return lhs.peerRank < rhs.peerRank; + }); + const uint64_t bytesPerBlock = TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + const uint32_t blocksPerChunk = static_cast(spec.chunkBytes / bytesPerBlock); + const size_t preSyncInstructions = MeshPreSyncInstructionCount(peers.size()); + const size_t copyInstructionsPerBlock = MeshCopyInstructionCountPerBlock(peers.size()); + program->reserve(preSyncInstructions + + static_cast(blocksPerChunk) * copyInstructionsPerBlock + peers.size() * 2U + 1U); + + ret = AppendMeshPeerPosts(peers, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + for (const auto& peer : peers) { + ret = AppendNotifyWait( + peer.route.preSyncLocalWaitCke, + static_cast(PreSyncSignalMask(peer.route) | PreSyncTokenMask(peer.route)), + "mesh PreSync", + false, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + const uint64_t selfBaseOffset = static_cast(spec.localRank) * spec.chunkBytes; + for (uint32_t block = 0; block < blocksPerChunk; ++block) { + for (size_t ordinal = 0; ordinal < peers.size(); ++ordinal) { + const auto& peer = peers[ordinal]; + TileXRCcuAllToAll2RankProgramSpec route = peer.route; + route.localSendAddr = spec.localSendAddr + static_cast(peer.peerRank) * spec.chunkBytes; + route.remoteRecvAddr += static_cast(spec.localRank) * spec.chunkBytes; + ret = AppendMeshRemoteCopyBlock( + route, + static_cast(block) * bytesPerBlock, + bytesPerBlock, + peer.route.copyCompletionCke, + static_cast(1U << (ordinal % TILEXR_CCU_CKE_MASK_BITS)), + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + ret = AppendLocalCopyBlock( + spec, + selfBaseOffset + static_cast(block) * bytesPerBlock, + bytesPerBlock, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + for (uint32_t group = 0; group < spec.remoteCompletionCkes.size(); ++group) { + ret = AppendNotifyWait( + spec.remoteCompletionCkes[group], + CompletionMaskForGroup(peers.size(), group), + "mesh Copy", + true, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + } + + for (const auto& peer : peers) { + ret = AppendMeshPostNotify(peer.route, program, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + for (const auto& peer : peers) { + ret = AppendNotifyWait( + peer.route.postSyncLocalWaitCke, + peer.route.ckeMask, + "mesh PostSync", + true, + program, + report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + TileXRCcuInstr finish; + if (TileXRCcuEncodeLoadImdToXn(spec.selfSourceXn, 0, 0, &finish) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode direct CCU alltoall mesh finish"); + } + program->push_back(finish); + + if (TileXRCcuValidateAllToAllMeshProgramBindings(spec, *program, report) != TILEXR_SUCCESS) { + program->clear(); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + if (report != nullptr) { + report->preSyncInstructionCount = static_cast(preSyncInstructions); + report->blockCount = blocksPerChunk; + report->bytesPerBlock = static_cast(bytesPerBlock); + report->copyInstructionCount = static_cast(blocksPerChunk * copyInstructionsPerBlock); + report->postSyncInstructionCount = static_cast(peers.size() * 2U); + report->finishInstructionCount = 1U; + report->totalInstructionCount = static_cast(program->size()); + report->peerCount = static_cast(peers.size()); + report->syncResourceCount = static_cast(peers.size()); + report->remoteBlockCount = static_cast(peers.size()) * blocksPerChunk; + report->selfBlockCount = blocksPerChunk; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_alltoall_program.h b/src/comm/ccu/tilexr_ccu_alltoall_program.h new file mode 100644 index 00000000..d02ab822 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_alltoall_program.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_ALLTOALL_PROGRAM_H +#define TILEXR_CCU_ALLTOALL_PROGRAM_H + +#include "ccu/tilexr_ccu_memory_program.h" + +#include +#include +#include + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES = 4096U; +constexpr uint32_t TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK = 8U; +constexpr uint32_t TILEXR_CCU_ALLTOALL_BLOCK_BYTES = + TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES * TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; +constexpr uint16_t TILEXR_CCU_ALLTOALL_OUTPUT_XN_ID = 1U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_TOKEN_XN_ID = 2U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_LOOP_MARKER_MASK = 1U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_POST_SYNC_ID = 3U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_POST_SYNC_MASK = 0x8U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_SIGNAL_MASK = 1U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_RANK0_SIGNAL_MASK = 1U; +constexpr uint16_t TILEXR_CCU_ALLTOALL_RANK1_SIGNAL_MASK = 2U; + +struct TileXRCcuAllToAll2RankProgramSpec { + uint32_t localRank = 0; + uint64_t localSendAddr = 0; + uint64_t localSendToken = 0; + uint64_t localRecvAddr = 0; + uint64_t localRecvToken = 0; + uint64_t remoteSendAddr = 0; + uint64_t remoteSendToken = 0; + uint64_t remoteRecvAddr = 0; + uint64_t remoteRecvToken = 0; + uint64_t bytes = 0; + uint32_t memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + uint32_t memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; + uint16_t localGsa = 0; + uint16_t remoteGsa = 0; + uint16_t localXn = 0; + uint16_t remoteXn = 0; + uint16_t lengthXn = 0; + uint16_t preSyncLocalAddrXn = 0; + uint16_t preSyncLocalTokenXn = 0; + uint16_t preSyncLocalMarkerXn = 0; + uint16_t preSyncRemoteMarkerXn = 0; + uint16_t preSyncMarkerArgIndex = 0; + uint16_t channelId = 0; + uint16_t preSyncMarkerChannelId = 0; + uint16_t preSyncChannelId = 0; + uint16_t preSyncTokenChannelId = 0; + uint16_t copyChannelId = 0; + uint16_t postSyncChannelId = 0; + uint16_t copyCompletionCke = 0; + uint16_t preSyncRemoteAddrXn = 0; + uint16_t preSyncRemoteTokenXn = 0; + uint16_t preSyncLocalWaitCke = 0; + uint16_t preSyncRemoteNotifyCke = 0; + uint16_t preSyncTokenLocalWaitCke = 0; + uint16_t preSyncRemoteTokenNotifyCke = 0; + uint16_t postSyncLocalWaitCke = 0; + uint16_t postSyncRemoteNotifyCke = 0; + uint16_t sourceCke = 0; + uint16_t ckeMask = 1; + bool preSyncNotify = true; + bool preSyncWait = true; + bool preSyncMarkerEnabled = false; + bool postSyncNotify = true; + bool postSyncWait = true; + bool emitFinish = true; +}; + +struct TileXRCcuAllToAllMeshPeerSpec { + uint32_t peerRank = 0; + TileXRCcuAllToAll2RankProgramSpec route; +}; + +struct TileXRCcuAllToAllMeshProgramSpec { + uint32_t rankSize = 2; + uint32_t localRank = 0; + uint64_t localSendAddr = 0; + uint64_t localSendToken = 0; + uint64_t localRecvAddr = 0; + uint64_t localRecvToken = 0; + uint64_t chunkBytes = 0; + uint16_t selfSourceGsa = 0; + uint16_t selfDestinationGsa = 0; + uint16_t selfSourceXn = 0; + uint16_t selfDestinationXn = 0; + uint16_t selfLengthXn = 0; + uint16_t selfChannelId = 0; + uint16_t selfCompletionCke = 0; + std::vector remoteCompletionCkes; + std::vector peers; +}; + +struct TileXRCcuAllToAllProgramReport { + uint32_t preSyncInstructionCount = 0; + uint32_t blockCount = 0; + uint32_t bytesPerBlock = 0; + uint32_t copyInstructionCount = 0; + uint32_t postSyncInstructionCount = 0; + uint32_t finishInstructionCount = 0; + uint32_t totalInstructionCount = 0; + uint32_t peerCount = 0; + uint32_t syncResourceCount = 0; + uint32_t remoteBlockCount = 0; + uint32_t selfBlockCount = 0; + std::string message; +}; + +int TileXRCcuBuildAllToAll2RankProgram( + const TileXRCcuAllToAll2RankProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report); + +int TileXRCcuBuildAllToAllMeshProgram( + const TileXRCcuAllToAllMeshProgramSpec& spec, + std::vector* program, + TileXRCcuAllToAllProgramReport* report); + +int TileXRCcuValidateAllToAllMeshProgramBindings( + const TileXRCcuAllToAllMeshProgramSpec& spec, + const std::vector& program, + TileXRCcuAllToAllProgramReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_ALLTOALL_PROGRAM_H diff --git a/src/comm/ccu/tilexr_ccu_backend.cpp b/src/comm/ccu/tilexr_ccu_backend.cpp new file mode 100644 index 00000000..27efa405 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_backend.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#include "ccu/tilexr_ccu_backend.h" + +#include "ccu/tilexr_ccu_collective_planner.h" +#include "ccu/tilexr_ccu_executor.h" +#include "ccu/tilexr_ccu_runtime_session.h" + +#include + +namespace TileXR { + +class TileXRCcuBackend::Impl { +public: + Impl(); + int Init(const TileXRCcuBackendOptions &options); + void Shutdown(); + bool Available() const; + bool Supports(const TileXRCcuCollectiveRequest &request) const; + int PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan); + int SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream); + int PrepareSignalWait(const TileXRCcuSignalWaitRequest &request, TileXRCcuSignalWaitPlan *plan); + int SubmitSignalWait( + const TileXRCcuSignalWaitPlan &plan, + aclrtStream stream, + TileXRCcuDirectSubmitReport *report); +#ifdef TILEXR_CCU_TESTING + bool RuntimeInitializedForTest() const; +#endif + +private: + int EnsureInternals(); + + std::unique_ptr runtimeSession_; + std::unique_ptr planner_; + std::unique_ptr executor_; +}; + +TileXRCcuBackend::Impl::Impl() + : runtimeSession_(new (std::nothrow) TileXRCcuRuntimeSession()), + planner_(new (std::nothrow) TileXRCcuCollectivePlanner()), + executor_(new (std::nothrow) TileXRCcuExecutor()) +{ +} + +int TileXRCcuBackend::Impl::EnsureInternals() +{ + if (runtimeSession_ == nullptr) { + runtimeSession_.reset(new (std::nothrow) TileXRCcuRuntimeSession()); + } + if (planner_ == nullptr) { + planner_.reset(new (std::nothrow) TileXRCcuCollectivePlanner()); + } + if (executor_ == nullptr) { + executor_.reset(new (std::nothrow) TileXRCcuExecutor()); + } + return runtimeSession_ == nullptr || planner_ == nullptr || executor_ == nullptr ? + TILEXR_ERROR_INTERNAL : + TILEXR_SUCCESS; +} + +int TileXRCcuBackend::Impl::Init(const TileXRCcuBackendOptions &options) +{ + const int ret = EnsureInternals(); + if (ret != TILEXR_SUCCESS) { + return ret; + } + if (planner_ != nullptr) { + planner_->Reset(); + } + return runtimeSession_->Init(options); +} + +void TileXRCcuBackend::Impl::Shutdown() +{ + if (planner_ != nullptr) { + planner_->Reset(); + } + if (runtimeSession_ != nullptr) { + runtimeSession_->Shutdown(); + } +} + +bool TileXRCcuBackend::Impl::Available() const +{ + return runtimeSession_ != nullptr && runtimeSession_->Available(); +} + +bool TileXRCcuBackend::Impl::Supports(const TileXRCcuCollectiveRequest &request) const +{ + return runtimeSession_ != nullptr && planner_ != nullptr && planner_->Supports(*runtimeSession_, request); +} + +int TileXRCcuBackend::Impl::PrepareCollective( + const TileXRCcuCollectiveRequest &request, + TileXRCcuCollectivePlan *plan) +{ + if (runtimeSession_ == nullptr || planner_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return planner_->PrepareCollective(*runtimeSession_, request, plan); +} + +int TileXRCcuBackend::Impl::SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream) +{ + if (runtimeSession_ == nullptr || executor_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return executor_->Submit(*runtimeSession_, plan, stream); +} + +int TileXRCcuBackend::Impl::PrepareSignalWait( + const TileXRCcuSignalWaitRequest &request, + TileXRCcuSignalWaitPlan *plan) +{ + if (plan == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *plan = TileXRCcuSignalWaitPlan {}; + if (runtimeSession_ == nullptr || planner_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + TileXRCcuDirectInstallReport report; + return planner_->PrepareSignalWait(*runtimeSession_, request, plan, &report); +} + +int TileXRCcuBackend::Impl::SubmitSignalWait( + const TileXRCcuSignalWaitPlan &plan, + aclrtStream stream, + TileXRCcuDirectSubmitReport *report) +{ + if (report != nullptr) { + *report = TileXRCcuDirectSubmitReport {}; + } + if (!plan.ready || plan.submitTasks.empty()) { + if (report != nullptr) { + report->message = "direct CCU signal/wait plan is not ready"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + return TileXRCcuSubmitPreparedTasks(plan.submitTasks, stream, nullptr, nullptr, report); +} + +#ifdef TILEXR_CCU_TESTING +bool TileXRCcuBackend::Impl::RuntimeInitializedForTest() const +{ + return Available(); +} +#endif + +TileXRCcuBackend::TileXRCcuBackend() : impl_(new (std::nothrow) Impl()) +{ +} + +TileXRCcuBackend::~TileXRCcuBackend() +{ + Shutdown(); +} + +int TileXRCcuBackend::Init(const TileXRCcuBackendOptions &options) +{ + if (impl_ == nullptr) { + impl_.reset(new (std::nothrow) Impl()); + if (impl_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + } + return impl_->Init(options); +} + +void TileXRCcuBackend::Shutdown() +{ + if (impl_ != nullptr) { + impl_->Shutdown(); + } +} + +bool TileXRCcuBackend::Available() const +{ + return impl_ != nullptr && impl_->Available(); +} + +bool TileXRCcuBackend::Supports(const TileXRCcuCollectiveRequest &request) const +{ + return impl_ != nullptr && impl_->Supports(request); +} + +int TileXRCcuBackend::PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan) +{ + if (impl_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return impl_->PrepareCollective(request, plan); +} + +int TileXRCcuBackend::SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream) +{ + if (impl_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return impl_->SubmitCollective(plan, stream); +} + +int TileXRCcuBackend::PrepareSignalWait( + const TileXRCcuSignalWaitRequest &request, + TileXRCcuSignalWaitPlan *plan) +{ + if (impl_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return impl_->PrepareSignalWait(request, plan); +} + +int TileXRCcuBackend::SubmitSignalWait( + const TileXRCcuSignalWaitPlan &plan, + aclrtStream stream, + TileXRCcuDirectSubmitReport *report) +{ + if (impl_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + return impl_->SubmitSignalWait(plan, stream, report); +} + +#ifdef TILEXR_CCU_TESTING +bool TileXRCcuBackend::RuntimeInitializedForTest() const +{ + return impl_ != nullptr && impl_->RuntimeInitializedForTest(); +} +#endif + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_backend.h b/src/comm/ccu/tilexr_ccu_backend.h new file mode 100644 index 00000000..8ede81c9 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_backend.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#ifndef TILEXR_CCU_BACKEND_H +#define TILEXR_CCU_BACKEND_H + +#include +#include +#include +#include +#include + +#include "ccu/tilexr_ccu_direct_orchestrator.h" +#include "acl/acl_base.h" +#include "tilexr_types.h" + +namespace TileXR { + +class TileXRSockExchange; +class TileXRCcuRuntimeSession; +class TileXRCcuCollectivePlanner; +class TileXRCcuExecutor; + +struct TileXRCcuBackendOptions { + int rank = 0; + int rankSize = 0; + int devId = 0; + std::string uid; + TileXRSockExchange *exchange = nullptr; +}; + +struct TileXRCcuCollectiveRequest { + TileXRType type = TileXRType::ALL_GATHER; + void *sendBuf = nullptr; + void *recvBuf = nullptr; + int64_t count = 0; + TileXRDataType dataType = TILEXR_DATA_TYPE_RESERVED; + TileXRReduceOp reduceOp = TILEXR_REDUCE_RESERVED; + int root = 0; + aclrtStream stream = nullptr; +}; + +struct TileXRCcuCollectivePlan { + bool ready = false; +}; + +enum class TileXRCcuSignalWaitRole { + Signal = 0, + Wait = 1, + SignalAndWait = 2, +}; + +struct TileXRCcuSignalWaitRequest { + int peerRank = -1; + TileXRCcuSignalWaitRole role = TileXRCcuSignalWaitRole::Signal; + bool overrideBarrierMode = false; + TileXRCcuBarrierMode barrierMode = TileXRCcuBarrierMode::SyncCke; + uint32_t syncInstructionCount = 0; + uint16_t missionStartId = 0; + uint16_t instructionStartId = 0; + uint16_t missionInstructionStartId = 0; + uint16_t xnStartId = 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; + uint16_t timeout = 0; + std::string provider; +}; + +struct TileXRCcuSignalWaitPlan { + bool ready = false; + TileXRCcuDirectInstallAttempt attempt; + std::vector submitTasks; +}; + +class TileXRCcuBackend { +public: + TileXRCcuBackend(); + ~TileXRCcuBackend(); + + TileXRCcuBackend(const TileXRCcuBackend&) = delete; + TileXRCcuBackend& operator=(const TileXRCcuBackend&) = delete; + + int Init(const TileXRCcuBackendOptions &options); + void Shutdown(); + bool Available() const; + bool Supports(const TileXRCcuCollectiveRequest &request) const; + int PrepareCollective(const TileXRCcuCollectiveRequest &request, TileXRCcuCollectivePlan *plan); + int SubmitCollective(const TileXRCcuCollectivePlan &plan, aclrtStream stream); + int PrepareSignalWait(const TileXRCcuSignalWaitRequest &request, TileXRCcuSignalWaitPlan *plan); + int SubmitSignalWait( + const TileXRCcuSignalWaitPlan &plan, + aclrtStream stream, + TileXRCcuDirectSubmitReport *report); +#ifdef TILEXR_CCU_TESTING + bool RuntimeInitializedForTest() const; +#endif + +private: + class Impl; + std::unique_ptr impl_; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_BACKEND_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 00000000..73b3a783 --- /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.sourceCkeMask; + 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 00000000..394b4680 --- /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_collective_planner.cpp b/src/comm/ccu/tilexr_ccu_collective_planner.cpp new file mode 100644 index 00000000..c160012d --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_collective_planner.cpp @@ -0,0 +1,1835 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#include "ccu/tilexr_ccu_collective_planner.h" + +#ifdef TILEXR_CCU_TESTING +#include "ccu/tilexr_ccu_memory_program.h" +#endif +#include "ccu/tilexr_ccu_repository.h" +#include "ccu/tilexr_ccu_runtime_session.h" +#include "tilexr_log.h" + +#include +#include +#include +#include +#include + +#ifdef TILEXR_CCU_TESTING +#include "runtime/dev.h" +#include "runtime/mem.h" +#include "runtime/rts/rts_device.h" +#endif + +namespace TileXR { + +#ifdef TILEXR_CCU_TESTING +constexpr uint32_t TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT = 7U; +constexpr uint32_t TILEXR_CCU_DIRECT_SYNC_XN_PING_INSTRUCTION_COUNT = 2U; +#endif +constexpr uint32_t TILEXR_CCU_DIRECT_SIGNAL_INSTRUCTION_COUNT = 5U; +constexpr uint32_t TILEXR_CCU_DIRECT_WAIT_INSTRUCTION_COUNT = 5U; +constexpr uint32_t TILEXR_CCU_DIRECT_SIGNAL_WAIT_INSTRUCTION_COUNT = 6U; +constexpr uint32_t TILEXR_CCU_CHANNEL_XN_STRIDE = 8U; + +uint32_t DirectAllToAll2RankInstructionCapacity(uint64_t bytes) +{ + if (bytes == 0 || bytes % TILEXR_CCU_ALLTOALL_BLOCK_BYTES != 0) { + return 0; + } + const uint64_t blocks = bytes / TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + const uint64_t instructions = 7ULL + blocks * 7ULL; + return instructions > std::numeric_limits::max() ? + 0U : static_cast(instructions); +} + +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 SelectDirectCcuChannelBoundRemoteXnOffset( + size_t peerLocalIndex, + size_t routeWithinPeer, + size_t routesPerPeer, + bool channelStrided) +{ + const size_t stride = channelStrided ? TILEXR_CCU_CHANNEL_XN_STRIDE : routesPerPeer; + return static_cast(peerLocalIndex * stride) + + static_cast(routeWithinPeer); +} + +uint16_t DirectCcuRemoteXnProofSpan(uint16_t syncRouteCount) +{ + if (syncRouteCount == 0) { + return 0; + } + return syncRouteCount; +} + +uint16_t SelectDirectCcuChannelBoundRemoteXnId( + uint16_t remoteXnStartId, + size_t peerLocalIndex, + size_t routeWithinPeer) +{ + return static_cast( + static_cast(remoteXnStartId) + + SelectDirectCcuChannelBoundRemoteXnOffset(peerLocalIndex, routeWithinPeer, 1U, true)); +} + +TileXRCcuSignalWaitProgramRole ToDirectSignalWaitProgramRole(TileXRCcuSignalWaitRole role) +{ + if (role == TileXRCcuSignalWaitRole::Wait) { + return TileXRCcuSignalWaitProgramRole::Wait; + } + if (role == TileXRCcuSignalWaitRole::SignalAndWait) { + return TileXRCcuSignalWaitProgramRole::SignalAndWait; + } + return TileXRCcuSignalWaitProgramRole::Signal; +} + +uint32_t DefaultSignalWaitInstructionCount(TileXRCcuSignalWaitRole role) +{ + if (role == TileXRCcuSignalWaitRole::Wait) { + return TILEXR_CCU_DIRECT_WAIT_INSTRUCTION_COUNT; + } + if (role == TileXRCcuSignalWaitRole::SignalAndWait) { + return TILEXR_CCU_DIRECT_SIGNAL_WAIT_INSTRUCTION_COUNT; + } + return TILEXR_CCU_DIRECT_SIGNAL_INSTRUCTION_COUNT; +} + +#ifdef TILEXR_CCU_TESTING +struct DirectCcuMemoryCopyEndpoint { + uint64_t sourceAddr = 0; + uint64_t sourceToken = 0; + TileXRCcuRemoteMemoryBufferImportRequest sourceRemoteImport {}; + uint64_t destinationAddr = 0; + uint64_t destinationToken = 0; + TileXRCcuRemoteMemoryBufferImportRequest destinationRemoteImport {}; + uint64_t bytes = 0; + uint32_t rank = 0; + uint32_t valid = 0; +}; + +bool TraceDirectCcuMemoryCopy() +{ + const char *trace = std::getenv("TILEXR_CCU_DIRECT_TRACE"); + return trace != nullptr && trace[0] != '\0' && trace[0] != '0'; +} + +bool UseCcuResourceWindowForMemoryCopy() +{ + const char *value = std::getenv("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_RESOURCE_WINDOW"); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +uint16_t SelectSyncXnPingMask(const char *envName) +{ + const char *text = std::getenv(envName); + 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 == 0 || parsed > 0xffffUL) { + return 0; + } + return static_cast(parsed); +} + +void TraceDirectCcuProcessMemoryToken( + uint64_t addr, + uint64_t bytes, + uint32_t rawTokenId, + uint32_t tokenId, + uint32_t tokenValue, + uint64_t packedToken) +{ + if (!TraceDirectCcuMemoryCopy()) { + return; + } + std::cerr << "TileXRDirectCcuTrace memoryToken" + << " addr=0x" << std::hex << addr + << " bytes=0x" << bytes + << " rawTokenId=0x" << rawTokenId + << " tokenId=0x" << tokenId + << " tokenValue=0x" << tokenValue + << " packed=0x" << packedToken + << std::dec << std::endl; +} + +void TraceDirectCcuRegisteredMemoryToken( + const char *label, + uint64_t addr, + uint64_t bytes, + const TileXRCcuRegisteredMemoryBufferInfo &info, + uint64_t packedToken) +{ + if (!TraceDirectCcuMemoryCopy()) { + return; + } + std::cerr << "TileXRDirectCcuTrace registeredMemoryToken" + << " label=" << (label == nullptr ? "" : label) + << " addr=0x" << std::hex << addr + << " bytes=0x" << bytes + << " alignedAddr=0x" << info.alignedAddr + << " alignedBytes=0x" << info.alignedBytes + << " targetSegVa=0x" << info.targetSegVa + << " rawTokenId=0x" << info.rawTokenId + << " tokenId=0x" << info.tokenId + << " tokenValue=0x" << info.tokenValue + << " packed=0x" << packedToken + << std::dec << std::endl; +} + +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; + } + const uint32_t tokenId = info.tokenId; + *packedToken = TileXRCcuPackMemoryToken(tokenId, info.tokenValue, true); + TraceDirectCcuProcessMemoryToken(addr, bytes, info.tokenId, tokenId, info.tokenValue, *packedToken); + return *packedToken == 0 ? TILEXR_ERROR_NOT_FOUND : TILEXR_SUCCESS; +} + +int BuildDirectCcuLocalMemoryCopyEndpoint( + TileXRCcuRuntimeSession &session, + 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; + TileXRCcuRegisteredMemoryBufferInfo sourceInfo; + int ret = session.RegisterMemoryBuffer(sourceAddr, bytes, &sourceInfo); + if (ret != TILEXR_SUCCESS) { + return ret; + } + if (sourceInfo.targetSegVa == 0) { + return TILEXR_ERROR_NOT_FOUND; + } + endpoint->sourceAddr = sourceInfo.addr; + endpoint->sourceToken = TileXRCcuPackMemoryToken(sourceInfo.tokenId, sourceInfo.tokenValue, true); + endpoint->sourceRemoteImport.addr = sourceInfo.addr; + endpoint->sourceRemoteImport.bytes = sourceInfo.bytes; + endpoint->sourceRemoteImport.alignedAddr = sourceInfo.alignedAddr; + endpoint->sourceRemoteImport.offset = sourceInfo.addr - sourceInfo.alignedAddr; + endpoint->sourceRemoteImport.tokenId = sourceInfo.tokenId; + endpoint->sourceRemoteImport.rawTokenId = sourceInfo.rawTokenId; + endpoint->sourceRemoteImport.tokenValue = sourceInfo.tokenValue; + endpoint->sourceRemoteImport.key = sourceInfo.key; + endpoint->sourceRemoteImport.valid = true; + TraceDirectCcuRegisteredMemoryToken("source", sourceAddr, bytes, sourceInfo, endpoint->sourceToken); + + TileXRCcuRegisteredMemoryBufferInfo destinationInfo; + ret = session.RegisterMemoryBuffer(destinationAddr, bytes, &destinationInfo); + if (ret != TILEXR_SUCCESS) { + return ret; + } + if (destinationInfo.targetSegVa == 0) { + return TILEXR_ERROR_NOT_FOUND; + } + endpoint->destinationAddr = destinationInfo.addr; + endpoint->destinationToken = + TileXRCcuPackMemoryToken(destinationInfo.tokenId, destinationInfo.tokenValue, true); + endpoint->destinationRemoteImport.addr = destinationInfo.addr; + endpoint->destinationRemoteImport.bytes = destinationInfo.bytes; + endpoint->destinationRemoteImport.alignedAddr = destinationInfo.alignedAddr; + endpoint->destinationRemoteImport.offset = destinationInfo.addr - destinationInfo.alignedAddr; + endpoint->destinationRemoteImport.tokenId = destinationInfo.tokenId; + endpoint->destinationRemoteImport.rawTokenId = destinationInfo.rawTokenId; + endpoint->destinationRemoteImport.tokenValue = destinationInfo.tokenValue; + endpoint->destinationRemoteImport.key = destinationInfo.key; + endpoint->destinationRemoteImport.valid = true; + TraceDirectCcuRegisteredMemoryToken( + "destination", + destinationAddr, + bytes, + destinationInfo, + endpoint->destinationToken); + endpoint->valid = 1; + return TILEXR_SUCCESS; +} +#endif + +void TileXRCcuCollectivePlanner::Reset() +{ + ResetDirectCcuLowerLayerPlan(); + directCcuLowerLayerTemplateConfigured_ = false; + directCcuLowerLayerTemplate_ = TileXRCcuLowerLayerTransportSnapshot {}; + directCcuVerifiedEndpointRoutes_.clear(); + directCcuLocalVerifiedEndpointRoute_ = TileXRCcuLowerLayerTransportRoute {}; + directCcuLocalVerifiedEndpointRouteValid_ = false; +} + +bool TileXRCcuCollectivePlanner::Supports( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request) const +{ + (void)session; + (void)request; + return false; +} + +int TileXRCcuCollectivePlanner::PrepareCollective( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request, + TileXRCcuCollectivePlan *plan) const +{ + if (plan == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *plan = TileXRCcuCollectivePlan {}; + if (!session.Available()) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + (void)request; + return TILEXR_ERROR_NOT_SUPPORT; +} + +void TileXRCcuCollectivePlanner::ResetDirectCcuLowerLayerPlan() +{ + directCcuLowerLayerPlanValid_ = false; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + directCcuLowerLayerSnapshot_ = TileXRCcuLowerLayerTransportSnapshot {}; + directCcuLowerLayerPlan_ = TileXRCcuLowerLayerInstallPlan {}; + directCcuLowerLayerPlanReport_ = TileXRCcuLowerLayerPlanBuilderReport {}; +} + +int TileXRCcuCollectivePlanner::ConfigureDirectCcuLowerLayerTemplate( + TileXRCcuRuntimeSession &session, + const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot) +{ + directCcuLowerLayerTemplate_ = templateSnapshot; + directCcuLowerLayerTemplateConfigured_ = true; + return RefreshDirectCcuLowerLayerPlan(session); +} + +int TileXRCcuCollectivePlanner::ConfigureDirectCcuVerifiedEndpointRoutes( + TileXRCcuRuntimeSession &session, + 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(session); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuCollectivePlanner::ConfigureDirectCcuLocalVerifiedEndpointRoute( + TileXRCcuRuntimeSession &session, + 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 (session.Available()) { + return session.ConfigureLocalVerifiedEndpointRoute(route); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuCollectivePlanner::ConfigureDirectCcuLowerLayerTemplateFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation, + const std::vector &remoteCcuBuffers) +{ + ResetDirectCcuLowerLayerPlan(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr) { + 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( + *basicInfo, + 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(session); +} + +int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerTemplateFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation) +{ + ResetDirectCcuLowerLayerPlan(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr) { + directCcuLowerLayerPlanReport_.message = + "direct CCU basic info is unavailable for lower-layer transport template"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + if (!session.Available()) { + directCcuLowerLayerPlanReport_.message = + "direct CCU runtime is unavailable for resource window registration"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + + int ret = session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to register direct CCU resource window"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLocalResourceWindowInfo localCcuResourceWindow; + ret = session.ExportLocalCcuRmaBuffer(&localCcuResourceWindow); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to export direct CCU local resource window token"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + if (directCcuLocalVerifiedEndpointRouteValid_) { + ret = session.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 = session.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 = session.ExportRemoteCcuRmaBuffers(&remoteCcuBuffers); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanReport_.message = "failed to export direct CCU peer resource window tokens"; + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + ret = ExchangeDirectCcuRemoteNotifyCke(session, allocation, &remoteCcuBuffers, &directCcuLowerLayerPlanReport_); + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } +#ifdef TILEXR_CCU_TESTING + ApplyDirectCcuRemoteRouteMemoryOverride(&remoteCcuBuffers); +#endif + + TileXRCcuLowerLayerTransportSnapshot templateSnapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + ret = TileXRCcuBuildLowerLayerTransportTemplate( + *basicInfo, + allocation, + remoteCcuBuffers, + &templateSnapshot, + &report); + directCcuLowerLayerPlanReport_ = report; + if (ret != TILEXR_SUCCESS) { + directCcuLowerLayerPlanStatus_ = ret; + return directCcuLowerLayerPlanStatus_; + } + templateSnapshot.msidToken.dieId = basicInfo->dieId; + templateSnapshot.msidToken.msId = basicInfo->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(session); +} + +int TileXRCcuCollectivePlanner::FillDirectCcuLowerLayerPlanFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report) +{ + if (plan == nullptr || report == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + int ret = PrepareDirectCcuLowerLayerTemplateFromAllocation(session, 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 TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke( + TileXRCcuRuntimeSession &session, + 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; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + 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.channels.num; + size_t routedPeerCount = peerRouteCount; + int selectedDiagnosticPeer = -1; +#ifdef TILEXR_CCU_TESTING + for (const auto &override : directCcuRemoteRouteMemoryOverrides_) { + if (!override.allRoutes && override.syncRouteIndex == 0U && + override.buffer.peerRank < static_cast(rankSize) && + override.buffer.peerRank != static_cast(rank)) { + selectedDiagnosticPeer = static_cast(override.buffer.peerRank); + if (syncRouteCount == 1U) { + routedPeerCount = 1U; + } + break; + } + } +#endif + const size_t routesPerPeer = syncRouteCount / routedPeerCount; + const bool channelStridedRemoteXn = allocation.channels.num != 0U && + static_cast(allocation.remoteXn.num) >= + static_cast(allocation.channels.num) * TILEXR_CCU_CHANNEL_XN_STRIDE; + if (allocation.localXn.num == 0 || + allocation.localWaitCke.num == 0 || + allocation.remoteNotifyCke.num == 0 || + allocation.remoteXn.num < routedPeerCount || + allocation.localWaitCke.num < syncRouteCount || + allocation.remoteNotifyCke.num < syncRouteCount || + allocation.channels.num == 0 || routesPerPeer == 0 || syncRouteCount % routedPeerCount != 0 || + routesPerPeer > TILEXR_CCU_CHANNEL_XN_STRIDE || + remoteCcuBuffers->size() != peerRouteCount) { + if (report != nullptr) { + report->message = "invalid direct CCU peer XN/CKE exchange shape" + " peerRoutes=" + std::to_string(peerRouteCount) + + " routedPeers=" + std::to_string(routedPeerCount) + + " syncRoutes=" + std::to_string(syncRouteCount) + + " localXn=" + std::to_string(allocation.localXn.num) + + " localWaitCke=" + std::to_string(allocation.localWaitCke.num) + + " remoteNotifyCke=" + std::to_string(allocation.remoteNotifyCke.num) + + " channels=" + std::to_string(allocation.channels.num) + + " peerBuffers=" + std::to_string(remoteCcuBuffers->size()); + } + 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 = session.AllGather(&local, sizeof(local), all.data()); + 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); + if (selectedDiagnosticPeer >= 0) { + peerRanks.push_back(selectedDiagnosticPeer); + } + for (int peer = 0; peer < rankSize; ++peer) { + if (peer != rank && peer != selectedDiagnosticPeer && peerRanks.size() < routedPeerCount) { + peerRanks.push_back(peer); + } + } + if (peerRanks.size() != routedPeerCount) { + if (report != nullptr) { + report->message = "invalid direct CCU peer XN/CKE exchange shape"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const std::vector peerCcuBuffers = *remoteCcuBuffers; + std::vector peerCcuBuffersByRank( + static_cast(rankSize), nullptr); + for (const auto &peerCcuBuffer : peerCcuBuffers) { + if (peerCcuBuffer.peerRank >= static_cast(rankSize) || + peerCcuBuffer.peerRank == static_cast(rank) || + peerCcuBuffersByRank[peerCcuBuffer.peerRank] != nullptr) { + if (report != nullptr) { + report->message = "invalid direct CCU peer buffer rank mapping"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + peerCcuBuffersByRank[peerCcuBuffer.peerRank] = &peerCcuBuffer; + } + for (const int peer : peerRanks) { + if (peerCcuBuffersByRank[static_cast(peer)] == nullptr) { + if (report != nullptr) { + report->message = "incomplete direct CCU peer buffer rank mapping"; + } + return TILEXR_ERROR_NOT_FOUND; + } + } + remoteCcuBuffers->assign(syncRouteCount, TileXRCcuRemoteCcuBufferInfo{}); + + size_t routeIndex = 0; + for (uint32_t syncIndex = 0; syncIndex < syncRouteCount; ++syncIndex) { + const size_t peerBufferIndex = syncIndex / routesPerPeer; + const size_t routeWithinPeer = syncIndex % routesPerPeer; + const int peer = peerRanks[peerBufferIndex]; + const PeerResourceExchange &peerResources = all[peer]; + const size_t peerLocalIndex = selectedDiagnosticPeer >= 0 ? + 0U : static_cast(rank < peer ? rank : rank - 1); + const uint32_t peerLocalResourceOffset = static_cast( + peerLocalIndex * routesPerPeer + routeWithinPeer); + const uint32_t peerLocalXnOffset = peerLocalResourceOffset; + const uint32_t selectedRemoteXnOffset = SelectDirectCcuChannelBoundRemoteXnOffset( + peerLocalIndex, + routeWithinPeer, + routesPerPeer, + channelStridedRemoteXn); + const uint32_t peerLocalWaitCkeOffset = peerLocalResourceOffset; + if (peerResources.localXnCount == 0 || + peerResources.remoteXnCount == 0 || + peerResources.localWaitCkeCount == 0 || + peerResources.remoteNotifyCkeCount == 0 || + peerResources.channelCount == 0 || + peerLocalXnOffset >= peerResources.localXnCount || + selectedRemoteXnOffset >= peerResources.remoteXnCount || + peerLocalResourceOffset >= peerResources.channelCount || + peerLocalWaitCkeOffset >= peerResources.localWaitCkeCount || + peerLocalWaitCkeOffset >= peerResources.remoteNotifyCkeCount) { + if (report != nullptr) { + report->message = "peer direct CCU local XN/CKE resources are incomplete"; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint16_t peerLocalXnId = + static_cast(static_cast(peerResources.localXnStartId) + peerLocalXnOffset); + const uint16_t channelBoundRemoteXnId = static_cast( + static_cast(peerResources.remoteXnStartId) + selectedRemoteXnOffset); + uint16_t remoteNotifyCke = + static_cast(static_cast(peerResources.localWaitCkeStartId) + + peerLocalWaitCkeOffset); + (*remoteCcuBuffers)[routeIndex] = *peerCcuBuffersByRank[static_cast(peer)]; + (*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 && + peerLocalResourceOffset < 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; +} + +#ifdef TILEXR_CCU_TESTING +void TileXRCcuCollectivePlanner::SetDirectCcuRemoteRouteMemoryOverride( + uint32_t peerRank, + uint64_t remoteCcuVa, + uint32_t memoryTokenId, + uint32_t rawMemoryTokenId, + uint32_t memoryTokenValue) +{ + directCcuRemoteRouteMemoryOverrides_.clear(); + DirectCcuRemoteRouteMemoryOverride override; + override.allRoutes = true; + override.applyMemory = true; + override.buffer.peerRank = peerRank; + override.buffer.remoteCcuVa = remoteCcuVa; + override.buffer.memoryTokenId = memoryTokenId; + override.buffer.rawMemoryTokenId = rawMemoryTokenId; + override.buffer.memoryTokenValue = memoryTokenValue; + if (remoteCcuVa != 0 && memoryTokenId != 0 && memoryTokenValue != 0) { + directCcuRemoteRouteMemoryOverrides_.push_back(override); + } +} + +void TileXRCcuCollectivePlanner::SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute( + uint32_t syncRouteIndex, + uint32_t peerRank, + uint64_t remoteCcuVa, + uint32_t memoryTokenId, + uint32_t rawMemoryTokenId, + uint32_t memoryTokenValue) +{ + DirectCcuRemoteRouteMemoryOverride override; + override.syncRouteIndex = syncRouteIndex; + override.buffer.peerRank = peerRank; + override.buffer.remoteCcuVa = remoteCcuVa; + override.buffer.memoryTokenId = memoryTokenId; + override.buffer.rawMemoryTokenId = rawMemoryTokenId; + override.buffer.memoryTokenValue = memoryTokenValue; + override.applyMemory = remoteCcuVa != 0 && memoryTokenId != 0 && memoryTokenValue != 0; + for (auto &existing : directCcuRemoteRouteMemoryOverrides_) { + if (!existing.allRoutes && existing.syncRouteIndex == syncRouteIndex) { + existing = override; + return; + } + } + directCcuRemoteRouteMemoryOverrides_.push_back(override); +} + +void TileXRCcuCollectivePlanner::ClearDirectCcuRemoteRouteMemoryOverride() +{ + directCcuRemoteRouteMemoryOverrides_.clear(); +} + +void TileXRCcuCollectivePlanner::ApplyDirectCcuRemoteRouteMemoryOverride( + std::vector *remoteCcuBuffers) const +{ + if (directCcuRemoteRouteMemoryOverrides_.empty() || remoteCcuBuffers == nullptr) { + return; + } + uint32_t routeIndex = 0; + for (auto &remoteCcuBuffer : *remoteCcuBuffers) { + for (const auto &override : directCcuRemoteRouteMemoryOverrides_) { + if (remoteCcuBuffer.peerRank != override.buffer.peerRank || + (!override.allRoutes && override.syncRouteIndex != routeIndex)) { + continue; + } + if (!override.applyMemory) { + break; + } + remoteCcuBuffer.remoteCcuVa = override.buffer.remoteCcuVa; + remoteCcuBuffer.memoryTokenId = override.buffer.memoryTokenId; + remoteCcuBuffer.rawMemoryTokenId = override.buffer.rawMemoryTokenId; + remoteCcuBuffer.memoryTokenValue = override.buffer.memoryTokenValue; + break; + } + ++routeIndex; + } +} +#endif + +int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report, + void *userData) +{ + auto *context = static_cast(userData); + if (context == nullptr || context->planner == nullptr || context->session == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return context->planner->FillDirectCcuLowerLayerPlanFromAllocation( + *context->session, + allocation, + plan, + report); +} + +int TileXRCcuCollectivePlanner::PrepareDirectCcuInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = "TileXRCcuBackend is not initialized for direct CCU install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const std::string unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int ret = session.RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return ret; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + 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 = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport{}; + report->message = adapterReport.message; + } + return ret; + } + + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = basicInfo; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = &callbackContext; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu"; + } + + return TileXRCcuRunDirectInstallAttempt(next, attempt, report); +} + +int TileXRCcuCollectivePlanner::PrepareSignalWait( + TileXRCcuRuntimeSession &session, + const TileXRCcuSignalWaitRequest &request, + TileXRCcuSignalWaitPlan *plan, + TileXRCcuDirectInstallReport *report) +{ + if (plan == nullptr) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "missing direct CCU signal/wait plan output"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *plan = TileXRCcuSignalWaitPlan {}; + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRCcuBackend is not initialized for direct CCU signal/wait"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + if (rankSize != 2 || request.peerRank < 0 || request.peerRank >= rankSize || request.peerRank == rank) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU signal/wait requires two ranks and a valid peer rank"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const std::string unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int ret = session.RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return ret; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for signal/wait"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + int ret = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions options; + options.basicInfo = basicInfo; + options.sqeArgCount = 0; + options.syncResourceCount = 1; + options.syncInstructionCount = request.syncInstructionCount == 0 ? + DefaultSignalWaitInstructionCount(request.role) : + request.syncInstructionCount; + options.bindingsPerSyncResource = 1; + options.missionStartId = request.missionStartId; + options.instructionStartId = request.instructionStartId; + options.missionInstructionStartId = request.missionInstructionStartId; + options.xnStartId = request.xnStartId; + options.remoteXnStartId = request.remoteXnStartId; + options.remoteXnCount = request.remoteXnCount; + options.ckeStartId = request.ckeStartId; + options.channelStartId = request.channelStartId; + options.localWaitCkeStartId = request.localWaitCkeStartId; + options.localWaitCkeCount = request.localWaitCkeCount; + options.remoteNotifyCkeStartId = request.remoteNotifyCkeStartId; + options.remoteNotifyCkeCount = request.remoteNotifyCkeCount; + options.taskTimeout = request.timeout; + options.deviceId = static_cast(session.DevId()); + options.rank = static_cast(rank); + options.provider = request.provider.empty() ? "tilexr-comm-direct-ccu-signal-wait" : request.provider; + options.offlineOnly = false; + options.driverAdapter = &adapter; + options.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(options.repositoryMemoryAllocMode); + options.repositoryMemoryUserData = nullptr; + options.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + options.lowerLayerPlanUserData = &callbackContext; + + TileXRCcuDirectSignalWaitSpec signalWait; + signalWait.role = ToDirectSignalWaitProgramRole(request.role); + signalWait.overrideBarrierMode = request.overrideBarrierMode; + signalWait.barrierMode = request.barrierMode; + ret = TileXRCcuRunDirectSignalWaitInstallAttempt(options, signalWait, &plan->attempt, report); + if (ret != TILEXR_SUCCESS) { + *plan = TileXRCcuSignalWaitPlan {}; + return ret; + } + plan->submitTasks = plan->attempt.submitTasks; + plan->ready = !plan->submitTasks.empty(); + return plan->ready ? TILEXR_SUCCESS : TILEXR_ERROR_NOT_FOUND; +} + +#ifdef TILEXR_CCU_TESTING +int TileXRCcuCollectivePlanner::PrepareDirectCcuMemoryCopyInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuMemoryCopyDirection direction, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRCcuBackend is not initialized for direct CCU memory copy install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + 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 unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int ret = session.RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return ret; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for memory copy install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + int ret = session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to register direct CCU resource window before memory copy buffer registration"; + } + return ret; + } + + DirectCcuMemoryCopyEndpoint localEndpoint; + ret = BuildDirectCcuLocalMemoryCopyEndpoint( + session, + 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 = session.AllGather( + &localEndpoint, + sizeof(localEndpoint), + allEndpoints.data()); + 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; + } + + TileXRCcuLocalResourceWindowInfo localResourceWindow; + std::vector allResourceWindows; + const bool useResourceWindow = UseCcuResourceWindowForMemoryCopy(); + if (useResourceWindow) { + ret = session.ExportLocalCcuRmaBuffer(&localResourceWindow); + if (ret != TILEXR_SUCCESS || localResourceWindow.addr == 0 || localResourceWindow.tokenId == 0) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to export direct CCU resource window for memory copy"; + } + return ret == TILEXR_SUCCESS ? TILEXR_ERROR_NOT_FOUND : ret; + } + allResourceWindows.resize(static_cast(rankSize)); + ret = session.AllGather( + &localResourceWindow, + sizeof(localResourceWindow), + allResourceWindows.data()); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to exchange direct CCU resource windows for memory copy"; + } + return ret; + } + } + + TileXRCcuDirectMemoryCopySpec memoryCopy; + memoryCopy.direction = direction; + memoryCopy.lengthBytes = bytes; + TileXRCcuImportedRemoteMemoryBufferInfo importedRemoteBuffer; + TileXRCcuRemoteMemoryBufferImportRequest remoteImportRequest; + if (direction == TileXRCcuMemoryCopyDirection::RemoteToLocal) { + remoteImportRequest = peerEndpoint.sourceRemoteImport; + ret = session.ImportRemoteMemoryBuffer(remoteImportRequest, &importedRemoteBuffer); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to import direct CCU memory copy remote source buffer"; + } + return ret; + } + memoryCopy.localAddr = localEndpoint.destinationAddr; + memoryCopy.localToken = localEndpoint.destinationToken; + if (useResourceWindow) { + const auto &peerWindow = allResourceWindows[peerRank]; + memoryCopy.remoteAddr = peerWindow.addr; + memoryCopy.remoteToken = TileXRCcuPackMemoryToken(peerWindow.tokenId, peerWindow.tokenValue, true); + } else { + memoryCopy.remoteAddr = remoteImportRequest.addr; + memoryCopy.remoteToken = + TileXRCcuPackMemoryToken(remoteImportRequest.tokenId, remoteImportRequest.tokenValue, true); + } + } else { + remoteImportRequest = peerEndpoint.destinationRemoteImport; + ret = session.ImportRemoteMemoryBuffer(remoteImportRequest, &importedRemoteBuffer); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to import direct CCU memory copy remote destination buffer"; + } + return ret; + } + memoryCopy.localAddr = localEndpoint.sourceAddr; + memoryCopy.localToken = localEndpoint.sourceToken; + if (useResourceWindow) { + const auto &peerWindow = allResourceWindows[peerRank]; + memoryCopy.remoteAddr = peerWindow.addr; + memoryCopy.remoteToken = TileXRCcuPackMemoryToken(peerWindow.tokenId, peerWindow.tokenValue, true); + } else { + memoryCopy.remoteAddr = remoteImportRequest.addr; + memoryCopy.remoteToken = + TileXRCcuPackMemoryToken(remoteImportRequest.tokenId, remoteImportRequest.tokenValue, true); + } + } + if (TraceDirectCcuMemoryCopy()) { + std::cerr << "TileXRDirectCcuTrace memoryCopyEndpoint" + << " rank=" << rank + << " peerRank=" << peerRank + << " direction=" << static_cast(direction) + << " localAddr=0x" << std::hex << memoryCopy.localAddr + << " localToken=0x" << memoryCopy.localToken + << " remoteAddr=0x" << memoryCopy.remoteAddr + << " remoteToken=0x" << memoryCopy.remoteToken + << " length=0x" << memoryCopy.lengthBytes + << std::dec << std::endl; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + ret = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = basicInfo; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = &callbackContext; + 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"; + } + + if (!useResourceWindow) { + SetDirectCcuRemoteRouteMemoryOverride( + peerRank, + importedRemoteBuffer.targetSegVa, + remoteImportRequest.tokenId, + remoteImportRequest.rawTokenId, + remoteImportRequest.tokenValue); + } + ret = TileXRCcuRunDirectMemoryCopyInstallAttempt(next, memoryCopy, attempt, report); + ClearDirectCcuRemoteRouteMemoryOverride(); + return ret; +} + +int TileXRCcuCollectivePlanner::PrepareDirectCcuAllToAll2RankInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRCcuBackend is not initialized for direct CCU alltoall install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + if (rankSize != 2 || localSourceAddr == 0 || localDestinationAddr == 0 || bytes == 0 || + peerRank >= static_cast(rankSize) || peerRank == static_cast(rank)) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU alltoall endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const std::string unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int ret = session.RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return ret; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for alltoall install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + int ret = session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to register direct CCU resource window before alltoall buffer registration"; + } + return ret; + } + + DirectCcuMemoryCopyEndpoint localEndpoint; + ret = BuildDirectCcuLocalMemoryCopyEndpoint( + session, + static_cast(rank), + localSourceAddr, + localDestinationAddr, + bytes, + &localEndpoint); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to query direct CCU alltoall local buffer token"; + } + return ret; + } + + std::vector allEndpoints(static_cast(rankSize)); + ret = session.AllGather( + &localEndpoint, + sizeof(localEndpoint), + allEndpoints.data()); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to exchange direct CCU alltoall 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 alltoall peer endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuImportedRemoteMemoryBufferInfo importedRemoteBuffer; + TileXRCcuRemoteMemoryBufferImportRequest remoteImportRequest = peerEndpoint.destinationRemoteImport; + ret = session.ImportRemoteMemoryBuffer(remoteImportRequest, &importedRemoteBuffer); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to import direct CCU alltoall remote destination buffer"; + } + return ret; + } + + TileXRCcuDirectAllToAll2RankSpec alltoall; + alltoall.localRank = static_cast(rank); + alltoall.localSendAddr = localEndpoint.sourceAddr; + alltoall.localSendToken = localEndpoint.sourceToken; + alltoall.localRecvAddr = localEndpoint.destinationAddr; + alltoall.localRecvToken = localEndpoint.destinationToken; + alltoall.remoteSendAddr = peerEndpoint.sourceAddr; + alltoall.remoteSendToken = peerEndpoint.sourceToken; + alltoall.remoteRecvAddr = remoteImportRequest.addr; + alltoall.remoteRecvToken = + TileXRCcuPackMemoryToken(remoteImportRequest.tokenId, remoteImportRequest.tokenValue, true); + alltoall.bytes = bytes; + alltoall.memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + alltoall.memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; + + if (TraceDirectCcuMemoryCopy()) { + std::cerr << "TileXRDirectCcuTrace alltoallEndpoint" + << " rank=" << rank + << " peerRank=" << peerRank + << " localSendAddr=0x" << std::hex << alltoall.localSendAddr + << " localSendToken=0x" << alltoall.localSendToken + << " localRecvAddr=0x" << alltoall.localRecvAddr + << " localRecvToken=0x" << alltoall.localRecvToken + << " remoteSendAddr=0x" << alltoall.remoteSendAddr + << " remoteSendToken=0x" << alltoall.remoteSendToken + << " remoteRecvAddr=0x" << alltoall.remoteRecvAddr + << " remoteRecvToken=0x" << alltoall.remoteRecvToken + << " bytes=0x" << alltoall.bytes + << std::dec << std::endl; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + ret = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = basicInfo; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = &callbackContext; + next.sqeArgCount = 0; + next.syncResourceCount = 3; + next.syncInstructionCount = std::max( + next.syncInstructionCount, + DirectAllToAll2RankInstructionCapacity(bytes)); + next.bindingsPerSyncResource = next.bindingsPerSyncResource == 0 ? 1 : next.bindingsPerSyncResource; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu-alltoall"; + } + + SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute( + 0U, + peerRank, + importedRemoteBuffer.targetSegVa, + remoteImportRequest.tokenId, + remoteImportRequest.rawTokenId, + remoteImportRequest.tokenValue); + ret = TileXRCcuRunDirectAllToAll2RankInstallAttempt(next, alltoall, attempt, report); + ClearDirectCcuRemoteRouteMemoryOverride(); + return ret; +} + +int TileXRCcuCollectivePlanner::PrepareDirectCcuAllToAllMeshInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t chunkBytes, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRCcuBackend is not initialized for direct CCU alltoall mesh install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + if (rankSize < 2 || rankSize > 64 || rank < 0 || rank >= rankSize || + localSourceAddr == 0 || localDestinationAddr == 0 || chunkBytes == 0 || + chunkBytes > std::numeric_limits::max() / static_cast(rankSize)) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU alltoall mesh endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const uint64_t bufferBytes = chunkBytes * static_cast(rankSize); + const std::string unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int refreshRet = session.RefreshDirectCcuBasicInfo(installDieId); + if (refreshRet != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return refreshRet; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for alltoall mesh install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + int ret = session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to register direct CCU resource window before alltoall mesh buffers"; + } + return ret; + } + DirectCcuMemoryCopyEndpoint localEndpoint; + ret = BuildDirectCcuLocalMemoryCopyEndpoint( + session, + static_cast(rank), + localSourceAddr, + localDestinationAddr, + bufferBytes, + &localEndpoint); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to query direct CCU alltoall mesh local buffer token"; + } + return ret; + } + + std::vector allEndpoints(static_cast(rankSize)); + ret = session.AllGather(&localEndpoint, sizeof(localEndpoint), allEndpoints.data()); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to exchange direct CCU alltoall mesh endpoints"; + } + return ret; + } + + TileXRCcuDirectAllToAllMeshSpec alltoall; + alltoall.rankSize = static_cast(rankSize); + alltoall.localRank = static_cast(rank); + alltoall.localSendAddr = localEndpoint.sourceAddr; + alltoall.localSendToken = localEndpoint.sourceToken; + alltoall.localRecvAddr = localEndpoint.destinationAddr; + alltoall.localRecvToken = localEndpoint.destinationToken; + alltoall.chunkBytes = chunkBytes; + for (uint32_t peerRank = 0; peerRank < static_cast(rankSize); ++peerRank) { + if (peerRank == static_cast(rank)) { + continue; + } + const DirectCcuMemoryCopyEndpoint &endpoint = allEndpoints[peerRank]; + if (endpoint.valid == 0 || endpoint.rank != peerRank || endpoint.bytes != bufferBytes || + !endpoint.destinationRemoteImport.valid) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU alltoall mesh peer endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const TileXRCcuRemoteMemoryBufferImportRequest &remoteImport = endpoint.destinationRemoteImport; + TileXRCcuImportedRemoteMemoryBufferInfo imported; + ret = session.ImportRemoteMemoryBuffer(remoteImport, &imported); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to import direct CCU alltoall mesh remote destination"; + } + return ret; + } + TileXRCcuDirectAllToAllMeshPeerSpec peer; + peer.peerRank = peerRank; + peer.remoteRecvAddr = remoteImport.addr; + peer.remoteRecvToken = TileXRCcuPackMemoryToken( + remoteImport.tokenId, remoteImport.tokenValue, true); + alltoall.peers.push_back(peer); + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + ret = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = basicInfo; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = &callbackContext; + next.sqeArgCount = 0; + next.syncResourceCount = static_cast(rankSize - 1); + next.bindingsPerSyncResource = 1U; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu-alltoall-mesh"; + } + + ClearDirectCcuRemoteRouteMemoryOverride(); + ret = TileXRCcuRunDirectAllToAllMeshInstallAttempt(next, alltoall, attempt, report); + ClearDirectCcuRemoteRouteMemoryOverride(); + return ret; +} + +int TileXRCcuCollectivePlanner::PrepareDirectCcuSyncXnPingInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report) +{ + if (!session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "TileXRCcuBackend is not initialized for direct CCU SyncXn ping install attempt"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + const int rank = session.Rank(); + const int rankSize = session.RankSize(); + if ((rankSize != 2 && rankSize != 4) || localSourceAddr == 0 || localDestinationAddr == 0 || bytes == 0 || + peerRank >= static_cast(rankSize) || peerRank == static_cast(rank)) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "invalid direct CCU SyncXn ping endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const std::string unavailableMessage = session.DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.empty()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = unavailableMessage; + } + return TILEXR_ERROR_NOT_FOUND; + } + const uint8_t installDieId = SelectDirectCcuInstallDieId(); + const TileXRCcuBasicInfo *basicInfo = session.GetDirectCcuBasicInfo(); + if (basicInfo == nullptr || basicInfo->dieId != installDieId) { + const int ret = session.RefreshDirectCcuBasicInfo(installDieId); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = session.GetDirectCcuBasicInfoReport().message; + } + return ret; + } + basicInfo = session.GetDirectCcuBasicInfo(); + } + if (basicInfo == nullptr || !session.Available()) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "direct CCU runtime is unavailable for SyncXn ping install attempt"; + } + return TILEXR_ERROR_NOT_FOUND; + } + + int ret = session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to register direct CCU resource window before SyncXn ping"; + } + return ret; + } + + DirectCcuMemoryCopyEndpoint localEndpoint; + ret = BuildDirectCcuLocalMemoryCopyEndpoint( + session, + static_cast(rank), + localSourceAddr, + localDestinationAddr, + bytes, + &localEndpoint); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to query direct CCU SyncXn ping local buffer token"; + } + return ret; + } + + std::vector allEndpoints(static_cast(rankSize)); + ret = session.AllGather( + &localEndpoint, + sizeof(localEndpoint), + allEndpoints.data()); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = "failed to exchange direct CCU SyncXn ping 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 SyncXn ping peer endpoint"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + ret = session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr) { + *report = TileXRCcuDirectInstallReport {}; + report->message = adapterReport.message; + } + return ret; + } + + LowerLayerPlanCallbackContext callbackContext {this, &session}; + TileXRCcuDirectInstallOptions next = options; + next.basicInfo = basicInfo; + next.offlineOnly = false; + next.driverAdapter = &adapter; + next.repositoryMemoryOps = TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode); + next.repositoryMemoryUserData = nullptr; + next.lowerLayerPlan = nullptr; + next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback; + next.lowerLayerPlanUserData = &callbackContext; + next.sqeArgCount = 0; + next.syncResourceCount = rankSize == 4 ? 3U : 1U; + next.syncInstructionCount = std::max( + next.syncInstructionCount, + TILEXR_CCU_DIRECT_SYNC_XN_PING_INSTRUCTION_COUNT); + next.bindingsPerSyncResource = next.bindingsPerSyncResource == 0 ? 1 : next.bindingsPerSyncResource; + if (next.provider.empty()) { + next.provider = "tilexr-comm-direct-ccu-sync-xn-ping"; + } + + TileXRCcuDirectSyncXnPingSpec syncXnPing; + syncXnPing.localRank = static_cast(rank); + syncXnPing.peerRank = peerRank; + syncXnPing.payload = 0x54585253594e0000ULL | static_cast(rank & 0xffff); + syncXnPing.remoteNotifyMask = + SelectSyncXnPingMask("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_NOTIFY_MASK"); + syncXnPing.localWaitMask = + SelectSyncXnPingMask("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_WAIT_MASK"); + + SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute(0U, peerRank, 0, 0, 0, 0); + ret = TileXRCcuRunDirectSyncXnPingInstallAttempt(next, syncXnPing, attempt, report); + ClearDirectCcuRemoteRouteMemoryOverride(); + return ret; +} +#endif + +int TileXRCcuCollectivePlanner::RefreshDirectCcuLowerLayerPlan(TileXRCcuRuntimeSession &session) +{ + ResetDirectCcuLowerLayerPlan(); + if (!directCcuLowerLayerTemplateConfigured_) { + directCcuLowerLayerPlanReport_.message = "direct CCU lower-layer template is not configured"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + if (!session.Available()) { + directCcuLowerLayerPlanReport_.message = "direct CCU runtime is unavailable for lower-layer planning"; + directCcuLowerLayerPlanStatus_ = TILEXR_ERROR_NOT_FOUND; + return directCcuLowerLayerPlanStatus_; + } + + TileXRCcuLowerLayerTransportSnapshot snapshot; + int ret = session.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 TileXRCcuCollectivePlanner::HasDirectCcuLowerLayerPlan() const +{ + return directCcuLowerLayerPlanValid_; +} + +int TileXRCcuCollectivePlanner::GetDirectCcuLowerLayerPlanStatus() const +{ + return directCcuLowerLayerPlanStatus_; +} + +const TileXRCcuLowerLayerPlanBuilderReport &TileXRCcuCollectivePlanner::GetDirectCcuLowerLayerPlanReport() const +{ + return directCcuLowerLayerPlanReport_; +} + +const TileXRCcuLowerLayerInstallPlan *TileXRCcuCollectivePlanner::GetDirectCcuLowerLayerPlan() const +{ + return directCcuLowerLayerPlanValid_ ? &directCcuLowerLayerPlan_ : nullptr; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_collective_planner.h b/src/comm/ccu/tilexr_ccu_collective_planner.h new file mode 100644 index 00000000..97f841ed --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_collective_planner.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#ifndef TILEXR_CCU_COLLECTIVE_PLANNER_H +#define TILEXR_CCU_COLLECTIVE_PLANNER_H + +#include +#include + +#include "ccu/tilexr_ccu_backend.h" +#include "ccu/tilexr_ccu_direct_orchestrator.h" +#ifdef TILEXR_CCU_TESTING +#include "ccu/tilexr_ccu_memory_program.h" +#endif + +namespace TileXR { + +class TileXRCcuRuntimeSession; + +class TileXRCcuCollectivePlanner { +public: + void Reset(); + bool Supports(const TileXRCcuRuntimeSession &session, const TileXRCcuCollectiveRequest &request) const; + int PrepareCollective( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectiveRequest &request, + TileXRCcuCollectivePlan *plan) const; + + int ConfigureDirectCcuLowerLayerTemplate( + TileXRCcuRuntimeSession &session, + const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot); + int ConfigureDirectCcuVerifiedEndpointRoutes( + TileXRCcuRuntimeSession &session, + const std::vector &verifiedRoutes); + int ConfigureDirectCcuLocalVerifiedEndpointRoute( + TileXRCcuRuntimeSession &session, + const TileXRCcuLowerLayerTransportRoute &route); + int ConfigureDirectCcuLowerLayerTemplateFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation, + const std::vector &remoteCcuBuffers); + int PrepareDirectCcuLowerLayerTemplateFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation); + int PrepareDirectCcuInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); + int PrepareSignalWait( + TileXRCcuRuntimeSession &session, + const TileXRCcuSignalWaitRequest &request, + TileXRCcuSignalWaitPlan *plan, + TileXRCcuDirectInstallReport *report); +#ifdef TILEXR_CCU_TESTING + int PrepareDirectCcuMemoryCopyInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuMemoryCopyDirection direction, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); + int PrepareDirectCcuAllToAll2RankInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); + int PrepareDirectCcuAllToAllMeshInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t chunkBytes, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); + int PrepareDirectCcuSyncXnPingInstallAttempt( + TileXRCcuRuntimeSession &session, + const TileXRCcuDirectInstallOptions &options, + uint64_t localSourceAddr, + uint64_t localDestinationAddr, + uint64_t bytes, + uint32_t peerRank, + TileXRCcuDirectInstallAttempt *attempt, + TileXRCcuDirectInstallReport *report); +#endif + int RefreshDirectCcuLowerLayerPlan(TileXRCcuRuntimeSession &session); + bool HasDirectCcuLowerLayerPlan() const; + int GetDirectCcuLowerLayerPlanStatus() const; + const TileXRCcuLowerLayerPlanBuilderReport &GetDirectCcuLowerLayerPlanReport() const; + const TileXRCcuLowerLayerInstallPlan *GetDirectCcuLowerLayerPlan() const; + +private: + struct LowerLayerPlanCallbackContext { + TileXRCcuCollectivePlanner *planner = nullptr; + TileXRCcuRuntimeSession *session = nullptr; + }; + + void ResetDirectCcuLowerLayerPlan(); + int FillDirectCcuLowerLayerPlanFromAllocation( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report); + int ExchangeDirectCcuRemoteNotifyCke( + TileXRCcuRuntimeSession &session, + const TileXRCcuResourceAllocation &allocation, + std::vector *remoteCcuBuffers, + TileXRCcuLowerLayerPlanBuilderReport *report); +#ifdef TILEXR_CCU_TESTING + void SetDirectCcuRemoteRouteMemoryOverride( + uint32_t peerRank, + uint64_t remoteCcuVa, + uint32_t memoryTokenId, + uint32_t rawMemoryTokenId, + uint32_t memoryTokenValue); + void SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute( + uint32_t syncRouteIndex, + uint32_t peerRank, + uint64_t remoteCcuVa, + uint32_t memoryTokenId, + uint32_t rawMemoryTokenId, + uint32_t memoryTokenValue); + void ClearDirectCcuRemoteRouteMemoryOverride(); + void ApplyDirectCcuRemoteRouteMemoryOverride( + std::vector *remoteCcuBuffers) const; +#endif + static int PrepareDirectCcuLowerLayerPlanCallback( + const TileXRCcuResourceAllocation &allocation, + TileXRCcuLowerLayerInstallPlan *plan, + TileXRCcuLowerLayerPlanBuilderReport *report, + void *userData); + + 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; +#ifdef TILEXR_CCU_TESTING + struct DirectCcuRemoteRouteMemoryOverride { + uint32_t syncRouteIndex = 0; + bool allRoutes = false; + bool applyMemory = false; + TileXRCcuRemoteCcuBufferInfo buffer; + }; + std::vector directCcuRemoteRouteMemoryOverrides_; +#endif +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_COLLECTIVE_PLANNER_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 00000000..953482ed --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_orchestrator.cpp @@ -0,0 +1,2189 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_direct_orchestrator.h" + +#include "ccu/tilexr_ccu_alltoall_program.h" +#include "ccu/tilexr_ccu_runtime.h" + +#include +#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_TRANS_LOC_MEM_TO_LOC_MEM_HEADER = 0x100aU; +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; +constexpr uint32_t TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT = 3U; +constexpr uint32_t TILEXR_CCU_DIRECT_ALLTOALL_MAX_RANK_SIZE = 64U; +constexpr uint32_t TILEXR_CCU_DIRECT_ALLTOALL_CKE_MASK_BITS = 16U; +constexpr uint32_t TILEXR_CCU_DIRECT_ALLTOALL_XN_BINDINGS_PER_CHANNEL = 3U; + +uint32_t DirectAllToAllMeshPeerCount(uint32_t rankSize) +{ + return rankSize >= 2U && rankSize <= TILEXR_CCU_DIRECT_ALLTOALL_MAX_RANK_SIZE ? rankSize - 1U : 0U; +} + +uint32_t DirectAllToAllMeshCompletionCkeCount(uint32_t rankSize) +{ + const uint32_t peers = DirectAllToAllMeshPeerCount(rankSize); + return peers == 0 ? 0U : (peers + TILEXR_CCU_DIRECT_ALLTOALL_CKE_MASK_BITS - 1U) / + TILEXR_CCU_DIRECT_ALLTOALL_CKE_MASK_BITS; +} +constexpr uint32_t TILEXR_CCU_DIRECT_SIGNAL_INSTRUCTION_COUNT = 5U; +constexpr uint32_t TILEXR_CCU_DIRECT_WAIT_INSTRUCTION_COUNT = 5U; +constexpr uint32_t TILEXR_CCU_DIRECT_SIGNAL_WAIT_INSTRUCTION_COUNT = 6U; +constexpr uint32_t TILEXR_CCU_DIRECT_SYNC_XN_PING_INSTRUCTION_COUNT = 2U; + +uint32_t DirectAllToAll2RankInstructionCapacity(uint64_t bytes) +{ + if (bytes == 0 || bytes % TILEXR_CCU_ALLTOALL_BLOCK_BYTES != 0) { + return 0; + } + const uint64_t blocks = bytes / TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + const uint64_t instructions = 7ULL + blocks * 7ULL; + return instructions > std::numeric_limits::max() ? + 0U : static_cast(instructions); +} + +uint32_t SyncXnPingAllocationInstructionCount(uint32_t syncResourceCount) +{ + if (syncResourceCount > std::numeric_limits::max() / 2U) { + return std::numeric_limits::max(); + } + return syncResourceCount * 2U; +} + +uint32_t DirectAllToAllMeshInstructionCount(uint32_t rankSize, uint64_t chunkBytes) +{ + const uint64_t peers = DirectAllToAllMeshPeerCount(rankSize); + const uint64_t completionCkes = DirectAllToAllMeshCompletionCkeCount(rankSize); + if (peers == 0 || chunkBytes == 0 || chunkBytes % TILEXR_CCU_ALLTOALL_BLOCK_BYTES != 0) { + return 0; + } + const uint64_t blocks = chunkBytes / TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + const uint64_t preSync = 3ULL + peers * 3ULL; + const uint64_t perBlock = peers * 6ULL + 9ULL + completionCkes; + const uint64_t postSync = peers * 2ULL; + const uint64_t instructions = preSync + blocks * perBlock + postSync + 1ULL; + return instructions > std::numeric_limits::max() ? 0U : static_cast(instructions); +} + +bool DirectAllToAllMeshCapacityFits( + const TileXRCcuResourceSpec& resources, + uint32_t rankSize, + uint32_t instructionCount, + std::string* message) +{ + const auto require = [message](const char* resource, uint32_t requested, uint32_t available) { + if (requested <= available) { + return true; + } + if (message != nullptr) { + std::ostringstream stream; + stream << "insufficient alltoall mesh " << resource + << " resources requested=" << requested + << " available=" << available; + *message = stream.str(); + } + return false; + }; + const uint32_t missionInstructionStart = resources.missionInstructionStartId == 0 ? + resources.instructionStartId : resources.missionInstructionStartId; + const uint32_t repositoryPrefix = missionInstructionStart - resources.instructionStartId; + const uint32_t localWaitCkeCount = resources.localWaitCkeCount == 0 ? + resources.ckeCount : resources.localWaitCkeCount; + const uint32_t remoteNotifyCkeCount = resources.remoteNotifyCkeCount == 0 ? + resources.ckeCount : resources.remoteNotifyCkeCount; + const uint32_t peers = DirectAllToAllMeshPeerCount(rankSize); + const uint32_t completionCkes = DirectAllToAllMeshCompletionCkeCount(rankSize); + const uint32_t localXns = std::max(peers, TILEXR_CCU_DIRECT_ALLTOALL_XN_BINDINGS_PER_CHANNEL); + const uint32_t remoteXns = TILEXR_CCU_DIRECT_ALLTOALL_XN_BINDINGS_PER_CHANNEL; + return peers != 0U && completionCkes != 0U && require("mission", 1U, resources.missionCount) && + require("instruction", repositoryPrefix + instructionCount, resources.instructionCount) && + require("GSA", TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT, resources.gsaCount) && + require("local XN", localXns, resources.xnCount) && + require("remote XN", remoteXns, + resources.remoteXnCount == 0 ? + (resources.xnCount > localXns ? resources.xnCount - localXns : 0U) : + resources.remoteXnCount) && + require("local CKE", peers + 1U + completionCkes, + localWaitCkeCount) && + require("remote CKE", peers, + remoteNotifyCkeCount) && + require("channel", peers, resources.channelCount); +} + +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_TRANS_LOC_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=TransLocMemToLocMem" + << " sourceGsa=" << TraceSlot(instr.words[0], 3) + << " sourceXn=" << TraceSlot(instr.words[1], 0) + << " destinationGsa=" << TraceSlot(instr.words[0], 1) + << " destinationXn=" << 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 ConfigureDirectAllToAll2RankResources( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAll2RankSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + const uint32_t instructionCapacity = DirectAllToAll2RankInstructionCapacity(alltoall.bytes); + if (attempt == nullptr || instructionCapacity == 0 || + attempt->plan.syncResources.size() != TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT || + attempt->plan.taskWindows.size() != 1) { + if (report != nullptr) { + report->message = "alltoall direct CCU plan requires three sync resources 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 = "alltoall 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 = "alltoall direct CCU local XN window is too small"; + } + 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->plan.kernelLocalXn = attempt->allocation.localXn; + attempt->plan.kernelLocalGsa = attempt->allocation.localGsa; + attempt->plan.taskWindows[0].instCnt = + static_cast(std::max( + attempt->plan.taskWindows[0].instCnt, + instructionCapacity)); + return TILEXR_SUCCESS; +} + +int ConfigureDirectAllToAllMeshResources( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAllMeshSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + const uint32_t peerCount = DirectAllToAllMeshPeerCount(alltoall.rankSize); + const uint32_t completionCkeCount = DirectAllToAllMeshCompletionCkeCount(alltoall.rankSize); + const uint32_t instructionCount = DirectAllToAllMeshInstructionCount(alltoall.rankSize, alltoall.chunkBytes); + if (attempt == nullptr || instructionCount == 0 || + attempt->plan.syncResources.size() != peerCount || + attempt->plan.taskWindows.size() != 1U) { + if (report != nullptr) { + report->message = "alltoall mesh direct CCU plan requires rankSize-1 peer resources 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 = "alltoall mesh direct CCU requires two kernel-local GSA resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (attempt->allocation.localXn.num < peerCount || attempt->allocation.remoteXn.num < peerCount || + attempt->allocation.localWaitCke.num < peerCount || attempt->allocation.remoteNotifyCke.num < peerCount || + attempt->allocation.channels.num < peerCount || + attempt->allocation.sourceCke.num < 1U + completionCkeCount) { + if (report != nullptr) { + report->message = "alltoall mesh direct CCU allocation is missing XN/CKE/channel resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + attempt->allocation.localGsa = MakeRange( + attempt->specInfo.dieId, + options.gsaStartId, + static_cast(TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT)); + attempt->plan.kernelLocalGsa = attempt->allocation.localGsa; + attempt->plan.taskWindows[0].instCnt = static_cast(instructionCount); + 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; + if (DirectTraceEnabled()) { + const auto tracePackedToken = [](uint64_t token, const char* label) { + std::cerr << " " << label << "=0x" << std::hex << token + << " " << label << "Valid=" << ((token >> TILEXR_CCU_PACKED_TOKEN_VALID_SHIFT) & 0x1ULL) + << " " << label << "Id=0x" + << ((token >> TILEXR_CCU_PACKED_TOKEN_ID_SHIFT) & TILEXR_CCU_PACKED_TOKEN_ID_MASK) + << " " << label << "Value=0x" + << (token & TILEXR_CCU_PACKED_TOKEN_VALUE_MASK) + << std::dec; + }; + std::cerr << "TileXRDirectCcuTrace memoryCopySpec" + << " direction=" << static_cast(copySpec.direction) + << " localGsa=" << copySpec.localGsa + << " localXn=" << copySpec.localXn + << " remoteGsa=" << copySpec.remoteGsa + << " remoteXn=" << copySpec.remoteXn + << " lengthXn=" << copySpec.lengthXn + << " channelId=" << copySpec.channelId + << " completionCke=" << copySpec.completionCke + << " completionMask=" << copySpec.completionMask + << " localAddr=0x" << std::hex << copySpec.localAddr + << " remoteAddr=0x" << copySpec.remoteAddr + << " lengthBytes=0x" << copySpec.lengthBytes + << std::dec; + tracePackedToken(copySpec.localToken, "localToken"); + tracePackedToken(copySpec.remoteToken, "remoteToken"); + std::cerr << std::endl; + } + + 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; +} + +int BuildDirectAllToAll2RankLaunchPackage( + const TileXRCcuDirectAllToAll2RankSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || + attempt->plan.syncResources.size() != TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT) { + if (report != nullptr) { + report->message = "missing direct CCU alltoall producer resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + 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 alltoall GSA/XN resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const TileXRCcuSyncResource& copyResource = attempt->plan.syncResources[0]; + const TileXRCcuSyncResource& preResource = attempt->plan.syncResources[1]; + const TileXRCcuSyncResource& postResource = attempt->plan.syncResources[2]; + const bool preSyncOnCopyRoute = + std::getenv("TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_ON_COPY_ROUTE") != nullptr; + const bool preSyncPeerLocalXn = + std::getenv("TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_PEER_LOCAL_XN") != nullptr; + const uint16_t preSyncRemoteAddrXn = + preSyncPeerLocalXn ? preResource.localXn : preResource.remoteXn; + + TileXRCcuAllToAll2RankProgramSpec alltoallSpec; + alltoallSpec.localRank = alltoall.localRank; + alltoallSpec.localSendAddr = alltoall.localSendAddr; + alltoallSpec.localSendToken = alltoall.localSendToken; + alltoallSpec.localRecvAddr = alltoall.localRecvAddr; + alltoallSpec.localRecvToken = alltoall.localRecvToken; + alltoallSpec.remoteSendAddr = alltoall.remoteSendAddr; + alltoallSpec.remoteSendToken = alltoall.remoteSendToken; + alltoallSpec.remoteRecvAddr = alltoall.remoteRecvAddr; + alltoallSpec.remoteRecvToken = alltoall.remoteRecvToken; + alltoallSpec.bytes = alltoall.bytes; + alltoallSpec.memorySliceBytes = alltoall.memorySliceBytes; + alltoallSpec.memSlicePerBlock = alltoall.memSlicePerBlock; + alltoallSpec.localGsa = attempt->plan.kernelLocalGsa.startId; + alltoallSpec.remoteGsa = static_cast(attempt->plan.kernelLocalGsa.startId + 1U); + alltoallSpec.localXn = attempt->plan.kernelLocalXn.startId; + alltoallSpec.remoteXn = static_cast(attempt->plan.kernelLocalXn.startId + 1U); + alltoallSpec.lengthXn = static_cast(attempt->plan.kernelLocalXn.startId + 2U); + alltoallSpec.preSyncLocalAddrXn = + preSyncOnCopyRoute ? copyResource.localXn : preResource.localXn; + alltoallSpec.preSyncLocalTokenXn = postResource.localXn; + alltoallSpec.preSyncLocalMarkerXn = copyResource.localXn; + alltoallSpec.preSyncRemoteMarkerXn = copyResource.remoteXn; + alltoallSpec.preSyncMarkerArgIndex = 0; + alltoallSpec.preSyncMarkerEnabled = true; + alltoallSpec.channelId = copyResource.channelId; + alltoallSpec.preSyncChannelId = + preSyncOnCopyRoute ? copyResource.channelId : preResource.channelId; + alltoallSpec.preSyncMarkerChannelId = alltoallSpec.preSyncChannelId; + alltoallSpec.preSyncTokenChannelId = preResource.channelId; + alltoallSpec.copyChannelId = copyResource.channelId; + alltoallSpec.postSyncChannelId = postResource.channelId; + alltoallSpec.preSyncRemoteAddrXn = + preSyncOnCopyRoute ? copyResource.localXn : preSyncRemoteAddrXn; + alltoallSpec.preSyncRemoteTokenXn = + preSyncPeerLocalXn ? postResource.localXn : postResource.remoteXn; + alltoallSpec.preSyncRemoteNotifyCke = + preSyncOnCopyRoute ? attempt->allocation.remoteNotifyCke.startId : preResource.notifyCke; + alltoallSpec.preSyncLocalWaitCke = + preSyncOnCopyRoute + ? (copyResource.localWaitCke == 0 ? copyResource.notifyCke : copyResource.localWaitCke) + : (preResource.localWaitCke == 0 ? preResource.notifyCke : preResource.localWaitCke); + alltoallSpec.preSyncRemoteTokenNotifyCke = preResource.notifyCke; + alltoallSpec.preSyncTokenLocalWaitCke = + preResource.localWaitCke == 0 ? preResource.notifyCke : preResource.localWaitCke; + alltoallSpec.copyCompletionCke = + copyResource.localWaitCke == 0 ? copyResource.notifyCke : copyResource.localWaitCke; + alltoallSpec.postSyncRemoteNotifyCke = postResource.notifyCke; + alltoallSpec.postSyncLocalWaitCke = + postResource.localWaitCke == 0 ? postResource.notifyCke : postResource.localWaitCke; + alltoallSpec.sourceCke = preResource.sourceCke; + alltoallSpec.ckeMask = preResource.remoteNotifyMask == 0 ? 1U : preResource.remoteNotifyMask; + alltoallSpec.preSyncNotify = std::getenv("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC") == nullptr; + alltoallSpec.preSyncWait = std::getenv("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC_WAIT") == nullptr; + alltoallSpec.postSyncNotify = false; + alltoallSpec.postSyncWait = false; + alltoallSpec.emitFinish = false; + + if (DirectTraceEnabled()) { + std::cerr << "TileXRDirectCcuTrace alltoallSpec" + << " direction=LocalToRemote" + << " localRank=" << alltoallSpec.localRank + << " localGsa=" << alltoallSpec.localGsa + << " localXn=" << alltoallSpec.localXn + << " remoteGsa=" << alltoallSpec.remoteGsa + << " remoteXn=" << alltoallSpec.remoteXn + << " lengthXn=" << alltoallSpec.lengthXn + << " preLocalAddrXn=" << alltoallSpec.preSyncLocalAddrXn + << " preLocalTokenXn=" << alltoallSpec.preSyncLocalTokenXn + << " preChannelId=" << alltoallSpec.preSyncChannelId + << " preTokenChannelId=" << alltoallSpec.preSyncTokenChannelId + << " copyChannelId=" << alltoallSpec.copyChannelId + << " postChannelId=" << alltoallSpec.postSyncChannelId + << " preNotifyCke=" << alltoallSpec.preSyncRemoteNotifyCke + << " preTokenNotifyCke=" << alltoallSpec.preSyncRemoteTokenNotifyCke + << " preTokenWaitCke=" << alltoallSpec.preSyncTokenLocalWaitCke + << " preRemoteAddrXn=" << alltoallSpec.preSyncRemoteAddrXn + << " preRemoteTokenXn=" << alltoallSpec.preSyncRemoteTokenXn + << " copyCompletionCke=" << alltoallSpec.copyCompletionCke + << " postNotifyCke=" << alltoallSpec.postSyncRemoteNotifyCke + << " preSyncNotify=" << (alltoallSpec.preSyncNotify ? 1 : 0) + << " preSyncWait=" << (alltoallSpec.preSyncWait ? 1 : 0) + << " preSyncOnCopyRoute=" << (preSyncOnCopyRoute ? 1 : 0) + << " preSyncPeerLocalXn=" << (preSyncPeerLocalXn ? 1 : 0) + << " postSyncNotify=" << (alltoallSpec.postSyncNotify ? 1 : 0) + << " postSyncWait=" << (alltoallSpec.postSyncWait ? 1 : 0) + << " emitFinish=" << (alltoallSpec.emitFinish ? 1 : 0) + << " localSendAddr=0x" << std::hex << alltoallSpec.localSendAddr + << " localRecvAddr=0x" << alltoallSpec.localRecvAddr + << " remoteSendAddr=0x" << alltoallSpec.remoteSendAddr + << " remoteRecvAddr=0x" << alltoallSpec.remoteRecvAddr + << " bytes=0x" << alltoallSpec.bytes + << std::dec << std::endl; + } + + TileXRCcuProgram program; + TileXRCcuAllToAllProgramReport alltoallReport; + if (TileXRCcuBuildAllToAll2RankProgram(alltoallSpec, &program.sync, &alltoallReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = alltoallReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (program.sync.empty() || program.sync.size() > std::numeric_limits::max()) { + if (report != nullptr) { + report->message = "invalid direct CCU alltoall instruction count"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + attempt->plan.taskWindows[0].instCnt = static_cast(program.sync.size()); + + 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; +} + +int ValidateDirectAllToAllMeshRouteResources( + const TileXRCcuAllToAllMeshProgramSpec& mesh, + const TileXRCcuProducerPlan& plan, + TileXRCcuDirectInstallReport* report) +{ + if (mesh.peers.size() != mesh.rankSize - 1U || plan.syncResources.size() != mesh.peers.size()) { + if (report != nullptr) { + report->message = "alltoall mesh route binding validation has an invalid shape"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + for (size_t ordinal = 0; ordinal < mesh.peers.size(); ++ordinal) { + const auto& resource = plan.syncResources[ordinal]; + const auto& route = mesh.peers[ordinal].route; + if (route.preSyncMarkerEnabled || + route.preSyncChannelId != resource.channelId || + route.preSyncTokenChannelId != resource.channelId || + route.copyChannelId != resource.channelId || + route.postSyncChannelId != resource.channelId || + route.preSyncLocalWaitCke != resource.localWaitCke || + route.preSyncTokenLocalWaitCke != resource.localWaitCke || + route.postSyncLocalWaitCke != resource.localWaitCke || + route.preSyncRemoteNotifyCke != resource.notifyCke || + route.preSyncRemoteTokenNotifyCke != resource.notifyCke || + route.postSyncRemoteNotifyCke != resource.notifyCke || + route.copyCompletionCke != + mesh.remoteCompletionCkes[ordinal / TILEXR_CCU_DIRECT_ALLTOALL_CKE_MASK_BITS]) { + if (report != nullptr) { + std::ostringstream stream; + stream << "alltoall mesh route binding mismatch peerRank=" << mesh.peers[ordinal].peerRank + << " ordinal=" << ordinal; + report->message = stream.str(); + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } + return TILEXR_SUCCESS; +} + +int BuildDirectAllToAllMeshLaunchPackage( + const TileXRCcuDirectAllToAllMeshSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || + attempt->plan.syncResources.size() != DirectAllToAllMeshPeerCount(alltoall.rankSize) || + attempt->plan.kernelLocalGsa.num < TILEXR_CCU_DIRECT_MEMORY_COPY_LOCAL_GSA_COUNT) { + if (report != nullptr) { + report->message = "missing direct CCU alltoall mesh producer resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + auto peers = alltoall.peers; + std::sort(peers.begin(), peers.end(), [](const TileXRCcuDirectAllToAllMeshPeerSpec& lhs, + const TileXRCcuDirectAllToAllMeshPeerSpec& rhs) { + return lhs.peerRank < rhs.peerRank; + }); + TileXRCcuAllToAllMeshProgramSpec mesh; + mesh.rankSize = alltoall.rankSize; + mesh.localRank = alltoall.localRank; + mesh.localSendAddr = alltoall.localSendAddr; + mesh.localSendToken = alltoall.localSendToken; + mesh.localRecvAddr = alltoall.localRecvAddr; + mesh.localRecvToken = alltoall.localRecvToken; + mesh.chunkBytes = alltoall.chunkBytes; + mesh.selfSourceGsa = attempt->plan.kernelLocalGsa.startId; + mesh.selfDestinationGsa = static_cast(attempt->plan.kernelLocalGsa.startId + 1U); + + const uint16_t localXnStart = attempt->allocation.localXn.startId; + const uint16_t remoteXnStart = attempt->allocation.remoteXn.startId; + const uint32_t completionCkeCount = DirectAllToAllMeshCompletionCkeCount(alltoall.rankSize); + for (uint32_t group = 0; group < completionCkeCount; ++group) { + mesh.remoteCompletionCkes.push_back( + static_cast(attempt->allocation.sourceCke.startId + 1U + group)); + } + for (uint32_t ordinal = 0; ordinal < peers.size(); ++ordinal) { + const TileXRCcuSyncResource& resource = attempt->plan.syncResources[ordinal]; + TileXRCcuAllToAllMeshPeerSpec peer; + peer.peerRank = peers[ordinal].peerRank; + auto& route = peer.route; + route.localRank = alltoall.localRank; + route.localSendAddr = alltoall.localSendAddr; + route.localSendToken = alltoall.localSendToken; + route.localRecvAddr = alltoall.localRecvAddr; + route.localRecvToken = alltoall.localRecvToken; + route.remoteRecvAddr = peers[ordinal].remoteRecvAddr; + route.remoteRecvToken = peers[ordinal].remoteRecvToken; + route.bytes = alltoall.chunkBytes; + route.localGsa = attempt->plan.kernelLocalGsa.startId; + route.remoteGsa = static_cast(attempt->plan.kernelLocalGsa.startId + 1U); + route.localXn = localXnStart; + route.remoteXn = static_cast(remoteXnStart + 2U); + route.lengthXn = static_cast(localXnStart + 2U); + route.preSyncLocalAddrXn = localXnStart; + route.preSyncLocalTokenXn = static_cast(localXnStart + 1U); + route.preSyncRemoteAddrXn = remoteXnStart; + route.preSyncRemoteTokenXn = static_cast(remoteXnStart + 1U); + route.preSyncMarkerEnabled = false; + route.preSyncChannelId = resource.channelId; + route.preSyncTokenChannelId = resource.channelId; + route.copyChannelId = resource.channelId; + route.postSyncChannelId = resource.channelId; + route.copyCompletionCke = + mesh.remoteCompletionCkes[ordinal / TILEXR_CCU_DIRECT_ALLTOALL_CKE_MASK_BITS]; + route.preSyncLocalWaitCke = resource.localWaitCke; + route.preSyncRemoteNotifyCke = resource.notifyCke; + route.preSyncTokenLocalWaitCke = resource.localWaitCke; + route.preSyncRemoteTokenNotifyCke = resource.notifyCke; + route.postSyncLocalWaitCke = resource.localWaitCke; + route.postSyncRemoteNotifyCke = resource.notifyCke; + route.sourceCke = attempt->allocation.sourceCke.startId; + route.ckeMask = static_cast(1U << TILEXR_CCU_ALLTOALL_POST_SYNC_ID); + route.preSyncNotify = true; + route.preSyncWait = true; + route.postSyncNotify = true; + route.postSyncWait = true; + route.emitFinish = false; + mesh.peers.push_back(peer); + } + mesh.selfSourceXn = localXnStart; + mesh.selfDestinationXn = static_cast(localXnStart + 1U); + mesh.selfLengthXn = static_cast(localXnStart + 2U); + mesh.selfChannelId = 0; + mesh.selfCompletionCke = attempt->plan.syncResources[0].localWaitCke; + + if (ValidateDirectAllToAllMeshRouteResources(mesh, attempt->plan, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuProgram program; + TileXRCcuAllToAllProgramReport alltoallReport; + if (TileXRCcuBuildAllToAllMeshProgram(mesh, &program.sync, &alltoallReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = alltoallReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (program.sync.empty() || program.sync.size() > std::numeric_limits::max()) { + if (report != nullptr) { + report->message = "invalid direct CCU alltoall mesh instruction count"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + attempt->plan.taskWindows[0].instCnt = static_cast(program.sync.size()); + + 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; +} + +uint32_t SignalWaitInstructionCount(TileXRCcuSignalWaitProgramRole role) +{ + if (role == TileXRCcuSignalWaitProgramRole::Wait) { + return TILEXR_CCU_DIRECT_WAIT_INSTRUCTION_COUNT; + } + if (role == TileXRCcuSignalWaitProgramRole::Signal) { + return TILEXR_CCU_DIRECT_SIGNAL_INSTRUCTION_COUNT; + } + return TILEXR_CCU_DIRECT_SIGNAL_WAIT_INSTRUCTION_COUNT; +} + +TileXRCcuBarrierMode SignalWaitBarrierMode(TileXRCcuSignalWaitProgramRole role) +{ + if (role == TileXRCcuSignalWaitProgramRole::Wait) { + return TileXRCcuBarrierMode::SyncCkePostOnly; + } + return role == TileXRCcuSignalWaitProgramRole::Signal ? + TileXRCcuBarrierMode::SyncCkePostOnly : + TileXRCcuBarrierMode::SyncCke; +} + +TileXRCcuBarrierMode EffectiveSignalWaitBarrierMode(const TileXRCcuDirectSignalWaitSpec& signalWait) +{ + return signalWait.overrideBarrierMode ? signalWait.barrierMode : SignalWaitBarrierMode(signalWait.role); +} + +uint32_t BarrierInstructionCount(TileXRCcuBarrierMode mode) +{ + switch (mode) { + case TileXRCcuBarrierMode::SyncCke: + case TileXRCcuBarrierMode::SyncCkeSetWait: + return 3U; + case TileXRCcuBarrierMode::SyncXnLoadPostOnly: + return 2U; + case TileXRCcuBarrierMode::SyncXn: + case TileXRCcuBarrierMode::LocalCke: + return 2U; + case TileXRCcuBarrierMode::SyncXnPostOnly: + case TileXRCcuBarrierMode::SyncCkePostOnly: + case TileXRCcuBarrierMode::LocalCkePostOnly: + return 1U; + default: + return 2U; + } +} + +uint32_t SignalWaitInstructionCount(const TileXRCcuDirectSignalWaitSpec& signalWait) +{ + if (signalWait.overrideBarrierMode) { + return BarrierInstructionCount(signalWait.barrierMode); + } + return SignalWaitInstructionCount(signalWait.role); +} + +int BuildDirectSignalWaitLaunchPackage( + const TileXRCcuDirectSignalWaitSpec& signalWait, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || attempt->plan.syncResources.empty()) { + if (report != nullptr) { + report->message = "missing direct CCU signal/wait producer resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const TileXRCcuSyncResource& resource = attempt->plan.syncResources[0]; + + TileXRCcuSignalWaitProgramSpec spec; + spec.role = signalWait.role; + spec.channelId = resource.channelId; + spec.remoteXn = resource.remoteXn; + spec.localXn = resource.localXn; + spec.localGsa = attempt->plan.kernelLocalGsa.num == 0 ? 0 : attempt->plan.kernelLocalGsa.startId; + spec.remoteNotifyCke = resource.notifyCke; + spec.remoteNotifyMask = resource.remoteNotifyMask == 0 ? 1U : resource.remoteNotifyMask; + spec.localWaitCke = resource.localWaitCke == 0 ? resource.notifyCke : resource.localWaitCke; + spec.localWaitMask = resource.localWaitMask == 0 ? 1U : resource.localWaitMask; + spec.sourceCke = resource.sourceCke; + spec.sourceCkeMask = spec.remoteNotifyMask; + + TileXRCcuProgram program; + TileXRCcuBarrierProgramReport signalWaitReport; + if (signalWait.overrideBarrierMode) { + TileXRCcuBarrierSyncSpec barrier; + barrier.channelId = spec.channelId; + barrier.remoteXn = spec.remoteXn; + barrier.localXn = spec.localXn; + barrier.remoteNotifyCke = spec.remoteNotifyCke; + barrier.remoteNotifyMask = spec.remoteNotifyMask; + barrier.localWaitCke = spec.localWaitCke; + barrier.localWaitMask = spec.localWaitMask; + barrier.sourceCke = spec.sourceCke; + barrier.sourceCkeMask = spec.sourceCkeMask; + barrier.clearLocalWait = spec.clearLocalWait; + std::vector barriers; + barriers.push_back(barrier); + if (TileXRCcuBuildBarrierProgram( + barriers, + &program.sync, + &signalWaitReport, + signalWait.barrierMode) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = signalWaitReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + } else if (TileXRCcuBuildSignalWaitProgram(spec, &program.sync, &signalWaitReport) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = signalWaitReport.message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (program.sync.empty() || program.sync.size() > std::numeric_limits::max()) { + if (report != nullptr) { + report->message = "invalid direct CCU signal/wait instruction count"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + attempt->plan.taskWindows[0].instCnt = static_cast(program.sync.size()); + + 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; +} + +int BuildDirectSyncXnPingLaunchPackage( + const TileXRCcuDirectSyncXnPingSpec& syncXnPing, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (attempt == nullptr || attempt->plan.syncResources.empty() || attempt->plan.taskWindows.size() != 1) { + if (report != nullptr) { + report->message = "missing direct CCU SyncXn ping producer resources"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (syncXnPing.localRank > 3U || syncXnPing.peerRank > 3U || syncXnPing.localRank == syncXnPing.peerRank) { + if (report != nullptr) { + report->message = "direct CCU SyncXn ping requires distinct rank ids in the range [0, 3]"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + const TileXRCcuSyncResource& resource = attempt->plan.syncResources[0]; + const uint16_t defaultRemoteNotifyMask = static_cast(1U << syncXnPing.localRank); + const uint16_t remoteNotifyMask = + syncXnPing.remoteNotifyMask == 0 ? defaultRemoteNotifyMask : syncXnPing.remoteNotifyMask; + if (resource.localXn == 0 || resource.remoteXn == 0 || resource.channelId == 0 || + resource.notifyCke == 0) { + if (report != nullptr) { + report->message = "missing direct CCU SyncXn ping XN/CKE/channel resource"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuProgram program; + program.sync.reserve(TILEXR_CCU_DIRECT_SYNC_XN_PING_INSTRUCTION_COUNT); + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(resource.localXn, syncXnPing.payload, 0, &instr) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = "failed to encode direct CCU SyncXn ping payload load"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + program.sync.push_back(instr); + + TileXRCcuSyncXnSpec notify; + notify.remoteXn = resource.remoteXn; + notify.localXn = resource.localXn; + notify.channelId = resource.channelId; + notify.notifyCke = resource.notifyCke; + notify.notifyMask = remoteNotifyMask; + notify.clearWait = true; + if (TileXRCcuEncodeSyncXn(notify, &instr) != TILEXR_SUCCESS) { + if (report != nullptr) { + report->message = "failed to encode direct CCU SyncXn ping notify"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + program.sync.push_back(instr); + attempt->plan.taskWindows[0].instCnt = static_cast(program.sync.size()); + + 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"; +} + +void ApplyTaskTimeoutOverride(uint16_t taskTimeout, TileXRCcuDirectInstallAttempt* attempt) +{ + if (taskTimeout == 0 || attempt == nullptr) { + return; + } + for (auto& task : attempt->package.tasks) { + task.timeout = taskTimeout; + } + for (auto& task : attempt->submitTasks) { + task.timeout = taskTimeout; + } +} + +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, + const TileXRCcuDirectAllToAll2RankSpec* alltoall, + const TileXRCcuDirectAllToAllMeshSpec* alltoallMesh, + const TileXRCcuDirectSignalWaitSpec* signalWait, + const TileXRCcuDirectSyncXnPingSpec* syncXnPing, + 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); + if (alltoallMesh != nullptr) { + std::string capacityMessage; + if (!DirectAllToAllMeshCapacityFits( + attempt->resourceSpec, + alltoallMesh->rankSize, + DirectAllToAllMeshInstructionCount(alltoallMesh->rankSize, alltoallMesh->chunkBytes), + &capacityMessage)) { + return Fail(attempt, report, capacityMessage); + } + } + + const bool customProgram = memoryCopy != nullptr || alltoall != nullptr || alltoallMesh != nullptr || + signalWait != nullptr || syncXnPing != nullptr; + attempt->resourceRequest.sqeArgCount = customProgram ? 0U : options.sqeArgCount; + attempt->resourceRequest.syncResourceCount = + alltoallMesh != nullptr ? DirectAllToAllMeshPeerCount(alltoallMesh->rankSize) : + alltoall != nullptr ? TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT : + syncXnPing != nullptr ? options.syncResourceCount : + customProgram ? 1U : options.syncResourceCount; + attempt->resourceRequest.syncInstructionCount = + memoryCopy != nullptr ? + std::max(options.syncInstructionCount, TILEXR_CCU_DIRECT_MEMORY_COPY_INSTRUCTION_COUNT) : + alltoall != nullptr ? + std::max( + options.syncInstructionCount, + DirectAllToAll2RankInstructionCapacity(alltoall->bytes)) : + alltoallMesh != nullptr ? + std::max(options.syncInstructionCount, + DirectAllToAllMeshInstructionCount(alltoallMesh->rankSize, alltoallMesh->chunkBytes)) : + signalWait != nullptr ? + std::max(options.syncInstructionCount, SignalWaitInstructionCount(*signalWait)) : + syncXnPing != nullptr ? + std::max( + options.syncInstructionCount, + SyncXnPingAllocationInstructionCount(options.syncResourceCount)) : + options.syncInstructionCount; + attempt->resourceRequest.bindingsPerSyncResource = alltoallMesh != nullptr ? + 1U : options.bindingsPerSyncResource; + attempt->resourceRequest.minimumLocalXnCount = + alltoallMesh != nullptr ? + TILEXR_CCU_DIRECT_ALLTOALL_XN_BINDINGS_PER_CHANNEL : 0U; + attempt->resourceRequest.minimumRemoteXnCount = + alltoallMesh != nullptr ? + TILEXR_CCU_DIRECT_ALLTOALL_XN_BINDINGS_PER_CHANNEL : 0U; + attempt->resourceRequest.sourceCkeCount = alltoallMesh != nullptr ? + 1U + DirectAllToAllMeshCompletionCkeCount(alltoallMesh->rankSize) : 1U; + attempt->resourceRequest.barrierMode = + alltoallMesh != nullptr ? TileXRCcuBarrierMode::SyncCke : + alltoall != nullptr ? TileXRCcuBarrierMode::SyncXn : + syncXnPing != nullptr ? TileXRCcuBarrierMode::SyncXn : + signalWait == nullptr ? options.barrierMode : EffectiveSignalWaitBarrierMode(*signalWait); + + 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); + } + } else if (alltoall != nullptr) { + ret = ConfigureDirectAllToAll2RankResources(options, *alltoall, attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + report == nullptr || report->message.empty() ? + "failed to configure direct CCU alltoall resources" : + report->message); + } + } else if (alltoallMesh != nullptr) { + ret = ConfigureDirectAllToAllMeshResources(options, *alltoallMesh, attempt, report); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + report == nullptr || report->message.empty() ? + "failed to configure direct CCU alltoall mesh resources" : + report->message); + } + } + attempt->plan.barrierMode = + alltoallMesh != nullptr ? TileXRCcuBarrierMode::SyncCke : + alltoall != nullptr ? TileXRCcuBarrierMode::SyncXn : + syncXnPing != nullptr ? TileXRCcuBarrierMode::SyncXn : + signalWait == nullptr ? attempt->plan.barrierMode : EffectiveSignalWaitBarrierMode(*signalWait); + + 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 (!customProgram) { + 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 ? + BuildDirectMemoryCopyLaunchPackage(*memoryCopy, attempt, report) : + alltoallMesh != nullptr ? + BuildDirectAllToAllMeshLaunchPackage(*alltoallMesh, attempt, report) : + alltoall != nullptr ? + BuildDirectAllToAll2RankLaunchPackage(*alltoall, attempt, report) : + signalWait != nullptr ? + BuildDirectSignalWaitLaunchPackage(*signalWait, attempt, report) : + syncXnPing != nullptr ? + BuildDirectSyncXnPingLaunchPackage(*syncXnPing, attempt, report) : + TileXRCcuBuildLaunchPackage(attempt->plan, &attempt->package, &packageReport); + if (ret != TILEXR_SUCCESS) { + return Fail( + attempt, + report, + !customProgram ? packageReport.message : + (report == nullptr || report->message.empty() ? + (memoryCopy != nullptr ? + "failed to build direct CCU memory copy launch package" : + alltoallMesh != nullptr ? + "failed to build direct CCU alltoall mesh launch package" : + alltoall != nullptr ? + "failed to build direct CCU alltoall launch package" : + syncXnPing != nullptr ? + "failed to build direct CCU SyncXn ping launch package" : + "failed to build direct CCU signal/wait launch package") : + report->message)); + } + + ApplyTaskTimeoutOverride(options.taskTimeout, attempt); + 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); + ApplyTaskTimeoutOverride(options.taskTimeout, attempt); + 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, nullptr, nullptr, nullptr, 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, nullptr, nullptr, nullptr, nullptr, attempt, report); +} + +int TileXRCcuRunDirectAllToAll2RankInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAll2RankSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + if (alltoall.localRank > 1U || + alltoall.localSendAddr == 0 || alltoall.localSendToken == 0 || + alltoall.localRecvAddr == 0 || alltoall.localRecvToken == 0 || + alltoall.remoteSendAddr == 0 || alltoall.remoteSendToken == 0 || + alltoall.remoteRecvAddr == 0 || alltoall.remoteRecvToken == 0 || + alltoall.bytes == 0 || alltoall.memorySliceBytes != TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES || + alltoall.memSlicePerBlock == 0 || + alltoall.memSlicePerBlock > TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK) { + ResetReport(report); + ClearAttempt(attempt); + if (report != nullptr) { + report->message = "invalid direct CCU alltoall address/token/slice inputs"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return RunDirectInstallAttemptImpl(options, nullptr, &alltoall, nullptr, nullptr, nullptr, attempt, report); +} + +int TileXRCcuRunDirectAllToAllMeshInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAllMeshSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + bool valid = alltoall.rankSize >= 2U && alltoall.rankSize <= TILEXR_CCU_DIRECT_ALLTOALL_MAX_RANK_SIZE && + alltoall.localRank < alltoall.rankSize && + alltoall.localSendAddr != 0 && alltoall.localSendToken != 0 && + alltoall.localRecvAddr != 0 && alltoall.localRecvToken != 0 && + DirectAllToAllMeshInstructionCount(alltoall.rankSize, alltoall.chunkBytes) != 0 && + alltoall.peers.size() == alltoall.rankSize - 1U; + if (valid) { + std::vector peerRanks(alltoall.rankSize, false); + for (const auto& peer : alltoall.peers) { + if (peer.peerRank >= alltoall.rankSize || peer.peerRank == alltoall.localRank || + peerRanks[peer.peerRank] || peer.remoteRecvAddr == 0 || peer.remoteRecvToken == 0) { + valid = false; + break; + } + peerRanks[peer.peerRank] = true; + } + } + if (!valid) { + ResetReport(report); + ClearAttempt(attempt); + if (report != nullptr) { + report->message = "invalid direct CCU alltoall mesh address/token/rank inputs"; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + return RunDirectInstallAttemptImpl(options, nullptr, nullptr, &alltoall, nullptr, nullptr, attempt, report); +} + +int TileXRCcuRunDirectSignalWaitInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectSignalWaitSpec& signalWait, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + return RunDirectInstallAttemptImpl(options, nullptr, nullptr, nullptr, &signalWait, nullptr, attempt, report); +} + +int TileXRCcuRunDirectSyncXnPingInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectSyncXnPingSpec& syncXnPing, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report) +{ + return RunDirectInstallAttemptImpl(options, nullptr, nullptr, nullptr, nullptr, &syncXnPing, 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 00000000..e5a32cfb --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_orchestrator.h @@ -0,0 +1,210 @@ +/* + * 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_alltoall_program.h" +#include "ccu/tilexr_ccu_memory_program.h" +#include "ccu/tilexr_ccu_signal_wait_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; + uint16_t taskTimeout = 0; + 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 TileXRCcuDirectAllToAll2RankSpec { + uint32_t localRank = 0; + uint64_t localSendAddr = 0; + uint64_t localSendToken = 0; + uint64_t localRecvAddr = 0; + uint64_t localRecvToken = 0; + uint64_t remoteSendAddr = 0; + uint64_t remoteSendToken = 0; + uint64_t remoteRecvAddr = 0; + uint64_t remoteRecvToken = 0; + uint64_t bytes = 0; + uint32_t memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + uint32_t memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; +}; + +struct TileXRCcuDirectAllToAllMeshPeerSpec { + uint32_t peerRank = 0; + uint64_t remoteRecvAddr = 0; + uint64_t remoteRecvToken = 0; +}; + +struct TileXRCcuDirectAllToAllMeshSpec { + uint32_t rankSize = 2; + uint32_t localRank = 0; + uint64_t localSendAddr = 0; + uint64_t localSendToken = 0; + uint64_t localRecvAddr = 0; + uint64_t localRecvToken = 0; + uint64_t chunkBytes = 0; + std::vector peers; +}; + +struct TileXRCcuDirectSignalWaitSpec { + TileXRCcuSignalWaitProgramRole role = TileXRCcuSignalWaitProgramRole::Signal; + bool overrideBarrierMode = false; + TileXRCcuBarrierMode barrierMode = TileXRCcuBarrierMode::SyncCke; +}; + +struct TileXRCcuDirectSyncXnPingSpec { + uint32_t localRank = 0; + uint32_t peerRank = 1; + uint64_t payload = 0; + uint16_t remoteNotifyMask = 0; + uint16_t localWaitMask = 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 TileXRCcuRunDirectAllToAll2RankInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAll2RankSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report); + +int TileXRCcuRunDirectAllToAllMeshInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectAllToAllMeshSpec& alltoall, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report); + +int TileXRCcuRunDirectSignalWaitInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectSignalWaitSpec& signalWait, + TileXRCcuDirectInstallAttempt* attempt, + TileXRCcuDirectInstallReport* report); + +int TileXRCcuRunDirectSyncXnPingInstallAttempt( + const TileXRCcuDirectInstallOptions& options, + const TileXRCcuDirectSyncXnPingSpec& syncXnPing, + 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 00000000..be8eafb9 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_runtime.cpp @@ -0,0 +1,2565 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_direct_runtime.h" + +#include "ccu/tilexr_ccu_topology.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 uint32_t TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM = 23; +constexpr uint32_t TILEXR_CCU_HCOMM_OUTER_FE_START_JETTY_CTX_ID = 92; +constexpr uint32_t TILEXR_CCU_HCOMM_OUTER_FE_JETTY_NUM = 36; +constexpr uint32_t TILEXR_CCU_HCOMM_MAX_INNER_FE_ID = 7; +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 uint8_t TILEXR_CCU_DIRECT_ENDPOINT_ERR_TIMEOUT = 16; +constexpr uint8_t TILEXR_CCU_DIRECT_CTP_ENDPOINT_ERR_TIMEOUT = 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_RESOURCE_WINDOW_EID_INDEX_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX"; +constexpr const char* TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_ENV = + "TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID"; +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_HCCL_ROOT_INFO_PATH = "/etc/hccl_rootinfo.json"; +constexpr const char* TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET_ENV = + "TILEXR_CCU_DIRECT_REMOTE_CCU_VA_OFFSET"; +constexpr const char* TILEXR_CCU_DIRECT_RECOVER_TASK_KILL_STATE_ENV = + "TILEXR_CCU_DIRECT_RECOVER_TASK_KILL_STATE"; +constexpr uint8_t TILEXR_CCU_DIRECT_DEFAULT_DIE_ID = 0; +constexpr uint64_t TILEXR_CCU_UB_MEM_PAGE_SIZE = 4096ULL; +constexpr uint32_t TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT = 8; +constexpr uint32_t TILEXR_CCU_TP_HANDLE_REQUEST_NUM = 8; +constexpr uint32_t TILEXR_CCU_TP_ATTR_BITMAP_SL = 1U << 10U; +constexpr uint32_t TILEXR_CCU_TP_ATTR_BITMAP_SL_AVAILABLE = 1U << 17U; +constexpr uint32_t TILEXR_CCU_DEFAULT_HCCL_QOS = 4; +constexpr uint32_t TILEXR_CCU_UBOE_DEV_FLAG_RIGHT_SHIFT = 19U; + +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; +} + +struct TileXRCcuPeerEndpointOffer { + uint64_t resourceAddr = 0; + uint32_t resourceTokenId = 0; + uint32_t resourceRawTokenId = 0; + uint32_t resourceTokenValue = 0; + uint32_t jettyTokenValue = 0; + std::array eid {}; + TileXRCcuHccpQpKey qpKey {}; + uint32_t psn = 0; + uint32_t funcId = 0; + bool funcIdValid = false; + bool valid = false; +}; + +bool SameEid( + const std::array& expected, + const TileXRCcuHccpEid& actual) +{ + return std::memcmp(expected.data(), actual.raw, expected.size()) == 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; +} + +uint8_t SelectDirectCcuCleanupDieId() +{ + const char* value = std::getenv("TILEXR_CCU_DIRECT_INSTALL_DIE_ID"); + if (value == nullptr || value[0] == '\0') { + return TILEXR_CCU_DIRECT_DEFAULT_DIE_ID; + } + char* end = nullptr; + const unsigned long parsed = std::strtoul(value, &end, 10); + return end != value && *end == '\0' && parsed <= UINT8_MAX ? + static_cast(parsed) : + TILEXR_CCU_DIRECT_DEFAULT_DIE_ID; +} + +bool RecoverTaskKillState() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_RECOVER_TASK_KILL_STATE_ENV); + return value != nullptr && value[0] != '\0' && value[0] != '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; +} + +std::array CopyRawEid(const TileXRCcuHccpEid& eid) +{ + std::array copied {}; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + copied[i] = eid.raw[i]; + } + return copied; +} + +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; + } +} + +void TraceTaskKillStep( + const char* step, + uint8_t dieId, + int ret, + const TileXRCcuDriverAdapterReport& report) +{ + if (!TraceEndpointRoute()) { + return; + } + std::cerr << "TileXRDirectCcuTrace taskKill" << step + << " dieId=" << static_cast(dieId) + << " ret=" << ret + << " opcode=" << report.opcode + << " driverRet=" << report.driverRet + << " opRet=" << report.opRet + << " message=\"" << report.message << "\"" + << std::endl; +} + +void TraceRaCtxEidInfos(const std::vector& eidInfos) +{ + if (!TraceEndpointRoute()) { + return; + } + for (size_t i = 0; i < eidInfos.size(); ++i) { + std::cerr << "TileXRDirectCcuTrace endpointRoute raCtxEidInfo" + << " ordinal=" << i + << " eidIndex=" << eidInfos[i].eidIndex + << " dieId=" << eidInfos[i].dieId + << " funcId=" << eidInfos[i].funcId + << " devFeature=0x" << std::hex << eidInfos[i].resv << std::dec + << " eid=" << FormatEndpointEid(CopyRawEid(eidInfos[i].eid)) + << std::endl; + } +} + +bool ParseEndpointEid(const char* value, std::array* eid); + +bool BuildRaCtxResourceWindowEidCandidates( + int rank, + uint8_t dieId, + const std::vector& eidInfos, + std::vector* candidates) +{ + if (eidInfos.empty() || candidates == nullptr) { + return false; + } + candidates->clear(); + TraceRaCtxEidInfos(eidInfos); + const char* configuredEid = SelectRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_ENV, rank); + if (configuredEid != nullptr) { + std::array expected {}; + if (!ParseEndpointEid(configuredEid, &expected)) { + return false; + } + for (const auto& eidInfo : eidInfos) { + if (CopyRawEid(eidInfo.eid) == expected) { + candidates->push_back(eidInfo); + return true; + } + } + return false; + } + const char* configured = SelectRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_ENV, rank); + if (configured != nullptr) { + uint64_t configuredIndex = 0; + if (!ParseUnsignedEnv(configured, &configuredIndex) || configuredIndex > 0xffffffffULL) { + return false; + } + for (const auto& eidInfo : eidInfos) { + if (eidInfo.eidIndex == static_cast(configuredIndex)) { + candidates->push_back(eidInfo); + return true; + } + } + return false; + } + for (auto it = eidInfos.rbegin(); it != eidInfos.rend(); ++it) { + const bool uboeOnly = + ((it->resv >> TILEXR_CCU_UBOE_DEV_FLAG_RIGHT_SHIFT) & 1U) != 0U; + if (it->dieId == dieId && !uboeOnly) { + candidates->push_back(*it); + } + } + return !candidates->empty(); +} + +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 HasRaCtxRemoteMemoryImportSymbols(const TileXRCcuHccpLoader& loader) +{ + return loader.RaCtxRmemImport != nullptr && + loader.RaCtxRmemUnimport != 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 CountAvailableSl(uint16_t mask) +{ + uint32_t count = 0; + for (uint32_t bit = 0; bit < 16U; ++bit) { + count += (mask & (1U << bit)) != 0U ? 1U : 0U; + } + return count; +} + +uint8_t SlAtRank(uint16_t mask, uint32_t rank) +{ + uint32_t seen = 0; + for (uint8_t bit = 0; bit < 16U; ++bit) { + if ((mask & (1U << bit)) != 0U && seen++ == rank) { + return bit; + } + } + return 0; +} + +bool MapQosToTpAndSl( + uint32_t qos, + uint32_t tpCount, + uint16_t slMask, + uint32_t* tpIndex, + uint8_t* mappedSl) +{ + if (tpIndex == nullptr || mappedSl == nullptr || tpCount == 0) { + return false; + } + const uint32_t slCount = CountAvailableSl(slMask); + const uint32_t k = std::min(tpCount, slCount); + if (k == 0) { + return false; + } + const uint32_t groupCount = std::min(8U, k); + const uint32_t q = qos & 7U; + const uint32_t group = k == 3U ? (q < 3U ? 0U : (q < 5U ? 1U : 2U)) : + (q * groupCount) / 8U; + const uint32_t slot = (group * k) / groupCount; + if (slot >= k || slot >= tpCount) { + return false; + } + const uint32_t slRank = (slCount - 1U) - slot; + *tpIndex = (k - 1U) - slot; + *mappedSl = SlAtRank(slMask, slRank); + return true; +} + +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; +} + +bool SelectEndpointRouteJettyCtxId(uint32_t pfeId, uint32_t peerOrdinal, uint16_t* jettyCtxId) +{ + if (jettyCtxId == nullptr) { + return false; + } + const uint32_t start = pfeId > TILEXR_CCU_HCOMM_MAX_INNER_FE_ID ? + TILEXR_CCU_HCOMM_OUTER_FE_START_JETTY_CTX_ID : + pfeId * TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM; + const uint32_t count = pfeId > TILEXR_CCU_HCOMM_MAX_INNER_FE_ID ? + TILEXR_CCU_HCOMM_OUTER_FE_JETTY_NUM : + TILEXR_CCU_HCOMM_INNER_FE_JETTY_NUM; + if (peerOrdinal >= count || start + peerOrdinal >= 128U) { + return false; + } + *jettyCtxId = static_cast(start + peerOrdinal); + return true; +} + +uint64_t SelectEndpointRouteSqVa( + const TileXRCcuLocalResourceWindowInfo& localResourceWindow, + uint16_t jettyCtxId) +{ + return localResourceWindow.addr + TILEXR_CCU_V1_WQE_BASIC_BLOCK_OFFSET + + static_cast(jettyCtxId) * 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; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + const uint8_t cleanupDieId = SelectDirectCcuCleanupDieId(); + int cleanupRet = CreateDriverAdapter(&adapter, &adapterReport); + if (cleanupRet == TILEXR_SUCCESS && RecoverTaskKillState()) { + cleanupRet = adapter.SetTaskKill(cleanupDieId, &adapterReport); + TraceTaskKillStep("Set", cleanupDieId, cleanupRet, adapterReport); + // hcomm treats SET_TASKKILL as a best-effort trigger and gates recovery + // on the following CLEAN_TASKKILL_STATE result. + cleanupRet = TILEXR_SUCCESS; + } + if (cleanupRet == TILEXR_SUCCESS) { + cleanupRet = adapter.CleanTaskKillState(cleanupDieId, &adapterReport); + } + TraceTaskKillStep("Cleanup", cleanupDieId, cleanupRet, adapterReport); + if (RecoverTaskKillState() && cleanupRet != TILEXR_SUCCESS) { + const std::string message = "failed to clean direct CCU task-kill state after explicit recovery: " + + adapterReport.message; + Shutdown(); + return Fail(report, message, cleanupRet); + } + 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(); + importedRemoteMemoryBuffers_.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; + } + if (resourceWindowRegistered_ && + localResourceWindow_.addr == resourceAddr && + localResourceWindow_.raCtxRegistered == IsRaCtxResourceWindowRegistrationMode()) { + return TILEXR_SUCCESS; + } + 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::RegisterMemoryBuffer( + uint64_t addr, + uint64_t bytes, + TileXRCcuRegisteredMemoryBufferInfo* info) +{ + if (info == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *info = TileXRCcuRegisteredMemoryBufferInfo {}; + if (!IsAvailable() || !resourceWindowRegistered_ || localResourceWindow_.raCtxHandle == nullptr) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (addr == 0 || bytes == 0 || !HasRaCtxResourceWindowSymbols(loader_)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + void* tokenIdHandle = nullptr; + void* lmemHandle = nullptr; + TileXRCcuHccpTokenId allocatedToken {}; + int ret = loader_.RaCtxTokenIdAlloc(localResourceWindow_.raCtxHandle, &allocatedToken, &tokenIdHandle); + if (ret != 0 || tokenIdHandle == nullptr) { + 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(localResourceWindow_.raCtxHandle, tokenIdHandle); + } + return TILEXR_ERROR_MKIRT; + } + + const uint64_t alignedAddr = AlignResourceWindowAddr(addr); + const uint64_t alignedBytes = bytes + (addr - 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(localResourceWindow_.raCtxHandle, &mr, &lmemHandle); + if (ret != 0 || lmemHandle == nullptr) { + if (lmemHandle != nullptr && loader_.RaCtxLmemUnregister != nullptr) { + (void)loader_.RaCtxLmemUnregister(localResourceWindow_.raCtxHandle, lmemHandle); + } + if (loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree(localResourceWindow_.raCtxHandle, tokenIdHandle); + } + return TILEXR_ERROR_MKIRT; + } + + TileXRCcuRegisteredMemoryBufferInfo registeredInfo {}; + registeredInfo.addr = addr; + registeredInfo.bytes = bytes; + registeredInfo.alignedAddr = alignedAddr; + registeredInfo.alignedBytes = alignedBytes; + registeredInfo.targetSegVa = mr.out.ub.targetSegHandle + (addr - alignedAddr); + registeredInfo.rawTokenId = mr.out.ub.tokenId != 0 ? mr.out.ub.tokenId : allocatedToken.tokenId; + registeredInfo.tokenId = registeredInfo.rawTokenId >> TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT; + registeredInfo.tokenValue = tokenValue; + registeredInfo.key = mr.out.key; + registeredInfo.tokenIdHandle = tokenIdHandle; + registeredInfo.lmemHandle = lmemHandle; + registeredInfo.valid = true; + + registeredMemoryBuffers_.push_back(registeredInfo); + *info = registeredInfo; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::ImportRemoteMemoryBuffer( + const TileXRCcuRemoteMemoryBufferImportRequest& request, + TileXRCcuImportedRemoteMemoryBufferInfo* info) +{ + if (info == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *info = TileXRCcuImportedRemoteMemoryBufferInfo {}; + if (!IsAvailable() || !resourceWindowRegistered_ || localResourceWindow_.raCtxHandle == nullptr) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (!request.valid || request.key.size == 0 || !HasRaCtxRemoteMemoryImportSymbols(loader_)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuHccpMrImportInfo mr {}; + mr.in.key = request.key; + mr.in.ub.tokenValue = request.tokenValue; + mr.in.ub.mappingAddr = 0; + mr.in.ub.flags.value = 0; + mr.in.ub.flags.bs.access = TILEXR_CCU_HCCP_MEM_SEG_ACCESS_DEFAULT; + + void* rmemHandle = nullptr; + const int ret = loader_.RaCtxRmemImport(localResourceWindow_.raCtxHandle, &mr, &rmemHandle); + if (ret != 0 || rmemHandle == nullptr || mr.out.ub.targetSegHandle == 0) { + if (rmemHandle != nullptr && loader_.RaCtxRmemUnimport != nullptr) { + (void)loader_.RaCtxRmemUnimport(localResourceWindow_.raCtxHandle, rmemHandle); + } + return TILEXR_ERROR_MKIRT; + } + + TileXRCcuImportedRemoteMemoryBufferInfo imported {}; + imported.addr = request.addr; + imported.bytes = request.bytes; + imported.targetSegVa = mr.out.ub.targetSegHandle + request.offset; + imported.rmemHandle = rmemHandle; + imported.valid = true; + importedRemoteMemoryBuffers_.push_back(imported); + *info = imported; + 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; + } + eidInfos.resize(queriedEidNum); + + void* ctxHandle = nullptr; + void* tokenIdHandle = nullptr; + void* lmemHandle = nullptr; + + std::vector eidCandidates; + if (!BuildRaCtxResourceWindowEidCandidates( + options_.rank, + SelectDirectCcuCleanupDieId(), + eidInfos, + &eidCandidates)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRCcuHccpCtxInitCfg ctxCfg {}; + ctxCfg.mode = TILEXR_CCU_NETWORK_OFFLINE; + ctxCfg.rdma.disabledLiteThread = false; + TileXRCcuHccpDevEidInfo selectedEid {}; + const bool explicitEid = + HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_ENV, options_.rank) || + HasRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_ENV, options_.rank); + const bool canProbeLoopTp = !explicitEid && + loader_.RaGetTpInfoListAsync != nullptr && loader_.RaGetAsyncReqResult != nullptr; + for (const auto& candidate : eidCandidates) { + TileXRCcuHccpCtxInitAttr ctxAttr {}; + ctxAttr.phyId = devicePhyId_; + ctxAttr.ub.eidIndex = candidate.eidIndex; + ctxAttr.ub.eid = candidate.eid; + ret = loader_.RaCtxInit(&ctxCfg, &ctxAttr, &ctxHandle); + if (ret != 0 || ctxHandle == nullptr) { + ctxHandle = nullptr; + continue; + } + uint64_t loopTpHandle = 0; + const auto candidateEid = CopyRawEid(candidate.eid); + const int probeRet = canProbeLoopTp ? + QueryTpHandleForPeer(ctxHandle, candidateEid, candidateEid, &loopTpHandle) : + TILEXR_SUCCESS; + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute loopEidCandidate" + << " eidIndex=" << candidate.eidIndex + << " dieId=" << candidate.dieId + << " funcId=" << candidate.funcId + << " eid=" << FormatEndpointEid(candidateEid) + << " probeRet=" << probeRet + << " tpHandle=0x" << std::hex << loopTpHandle << std::dec + << std::endl; + } + if (probeRet == TILEXR_SUCCESS) { + selectedEid = candidate; + break; + } + (void)loader_.RaCtxDeinit(ctxHandle); + ctxHandle = nullptr; + } + if (ctxHandle == nullptr) { + return TILEXR_ERROR_NOT_FOUND; + } + + 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] = selectedEid.eid.raw[i]; + } + localResourceWindow_.eidIndex = selectedEid.eidIndex; + localResourceWindow_.funcId = selectedEid.funcId; + localResourceWindow_.funcIdValid = true; + localResourceWindow_.raCtxRegistered = true; + resourceWindowRegistered_ = true; + return TILEXR_SUCCESS; +} + +void TileXRCcuDirectRuntime::ReleaseRegisteredResourceWindow() +{ + ReleaseImportedRemoteMemoryBuffers(); + ReleaseRegisteredMemoryBuffers(); + 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::ReleaseImportedRemoteMemoryBuffers() +{ + if (localResourceWindow_.raCtxHandle != nullptr && loader_.RaCtxRmemUnimport != nullptr) { + for (auto it = importedRemoteMemoryBuffers_.rbegin(); it != importedRemoteMemoryBuffers_.rend(); ++it) { + if (it->rmemHandle != nullptr) { + (void)loader_.RaCtxRmemUnimport(localResourceWindow_.raCtxHandle, it->rmemHandle); + } + } + } + importedRemoteMemoryBuffers_.clear(); +} + +void TileXRCcuDirectRuntime::ReleaseRegisteredMemoryBuffers() +{ + if (localResourceWindow_.raCtxHandle != nullptr) { + for (auto it = registeredMemoryBuffers_.rbegin(); it != registeredMemoryBuffers_.rend(); ++it) { + if (it->lmemHandle != nullptr && loader_.RaCtxLmemUnregister != nullptr) { + (void)loader_.RaCtxLmemUnregister(localResourceWindow_.raCtxHandle, it->lmemHandle); + } + if (it->tokenIdHandle != nullptr && loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree(localResourceWindow_.raCtxHandle, it->tokenIdHandle); + } + } + } + registeredMemoryBuffers_.clear(); +} + +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::ReleasePeerEndpointState(TileXRCcuPeerEndpointState* state) +{ + if (state == nullptr) { + return; + } + if (state->remoteQpHandle != nullptr && state->resourceWindow.raCtxHandle != nullptr && + loader_.RaCtxQpUnimport != nullptr) { + (void)loader_.RaCtxQpUnimport(state->resourceWindow.raCtxHandle, state->remoteQpHandle); + } + if (state->qpHandle != nullptr && loader_.RaCtxQpDestroy != nullptr) { + (void)loader_.RaCtxQpDestroy(state->qpHandle); + } + if (state->cqHandle != nullptr && state->resourceWindow.raCtxHandle != nullptr && + loader_.RaCtxCqDestroy != nullptr) { + (void)loader_.RaCtxCqDestroy(state->resourceWindow.raCtxHandle, state->cqHandle); + } + if (state->resourceWindow.lmemHandle != nullptr && state->resourceWindow.raCtxHandle != nullptr && + loader_.RaCtxLmemUnregister != nullptr) { + (void)loader_.RaCtxLmemUnregister( + state->resourceWindow.raCtxHandle, + state->resourceWindow.lmemHandle); + } + if (state->resourceWindow.tokenIdHandle != nullptr && state->resourceWindow.raCtxHandle != nullptr && + loader_.RaCtxTokenIdFree != nullptr) { + (void)loader_.RaCtxTokenIdFree( + state->resourceWindow.raCtxHandle, + state->resourceWindow.tokenIdHandle); + } + if (state->resourceWindow.raCtxHandle != nullptr && loader_.RaCtxDeinit != nullptr) { + (void)loader_.RaCtxDeinit(state->resourceWindow.raCtxHandle); + } + *state = TileXRCcuPeerEndpointState {}; +} + +void TileXRCcuDirectRuntime::ReleasePeerEndpointRoutes() +{ + for (auto it = peerEndpointStates_.rbegin(); it != peerEndpointStates_.rend(); ++it) { + ReleasePeerEndpointState(&*it); + } + peerEndpointStates_.clear(); +} + +void TileXRCcuDirectRuntime::ReleaseLocalEndpointRoute() +{ + ReleasePeerEndpointRoutes(); + 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_, + TILEXR_CCU_DIRECT_LOOP_JETTY_CTX_ID); + 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.errTimeout = TILEXR_CCU_DIRECT_ENDPOINT_ERR_TIMEOUT; + 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) + << " id=" << qpInfo.ub.id + << " 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.startJettyId = static_cast(qpInfo.ub.id); + 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 + << " startJettyId=" << collected.startJettyId + << " 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); + } + if (options_.rankSize > 2) { + return PreparePeerEndpointRoutes(report); + } + + 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::CreatePeerEndpointState( + uint32_t peerRank, + uint32_t peerDevicePhyId, + const std::array& localEid, + const std::array& peerEid, + uint32_t tpType, + uint32_t peerOrdinal, + TileXRCcuPeerEndpointState* state) +{ + if (state == nullptr || !resourceWindowRegistered_ || localResourceWindow_.addr == 0 || + !HasRaCtxResourceWindowSymbols(loader_) || !HasRaCtxEndpointRouteSymbols(loader_)) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *state = TileXRCcuPeerEndpointState {}; + state->peerRank = peerRank; + state->peerDevicePhyId = peerDevicePhyId; + state->tpType = tpType; + if (tpType != TILEXR_CCU_HCCP_TP_TYPE_RTP && tpType != TILEXR_CCU_HCCP_TP_TYPE_CTP) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + 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 TILEXR_ERROR_MKIRT; + } + std::vector eidInfos(eidNum); + uint32_t queriedEidNum = eidNum; + ret = loader_.RaGetDevEidInfoList(raInfo, eidInfos.data(), &queriedEidNum); + if (ret != 0 || queriedEidNum == 0) { + return TILEXR_ERROR_MKIRT; + } + const auto eidIt = std::find_if( + eidInfos.begin(), + eidInfos.begin() + queriedEidNum, + [&localEid](const TileXRCcuHccpDevEidInfo& info) { + return SameEid(localEid, info.eid); + }); + if (eidIt == eidInfos.begin() + queriedEidNum) { + return TILEXR_ERROR_NOT_FOUND; + } + state->eidInfo = *eidIt; + + TileXRCcuHccpCtxInitCfg ctxCfg {}; + ctxCfg.mode = TILEXR_CCU_NETWORK_OFFLINE; + ctxCfg.rdma.disabledLiteThread = false; + TileXRCcuHccpCtxInitAttr ctxAttr {}; + ctxAttr.phyId = devicePhyId_; + ctxAttr.ub.eidIndex = state->eidInfo.eidIndex; + ctxAttr.ub.eid = state->eidInfo.eid; + ret = loader_.RaCtxInit(&ctxCfg, &ctxAttr, &state->resourceWindow.raCtxHandle); + if (ret != 0 || state->resourceWindow.raCtxHandle == nullptr) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_MKIRT; + } + + TileXRCcuHccpTokenId allocatedToken {}; + ret = loader_.RaCtxTokenIdAlloc( + state->resourceWindow.raCtxHandle, + &allocatedToken, + &state->resourceWindow.tokenIdHandle); + if (ret != 0 || state->resourceWindow.tokenIdHandle == nullptr) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_MKIRT; + } + TileXRCcuRaInfo randomInfo {}; + randomInfo.mode = TILEXR_CCU_NETWORK_OFFLINE; + randomInfo.phyId = devicePhyId_; + ret = loader_.RaGetSecRandom(&randomInfo, &state->jettyTokenValue); + if (ret != 0) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_MKIRT; + } + state->resourceWindow.addr = localResourceWindow_.addr; + state->resourceWindow.bytes = localResourceWindow_.bytes; + state->resourceWindow.rawTokenId = localResourceWindow_.rawTokenId; + state->resourceWindow.tokenId = localResourceWindow_.tokenId; + state->resourceWindow.tokenValue = localResourceWindow_.tokenValue; + state->resourceWindow.targetSegHandle = localResourceWindow_.targetSegHandle; + state->resourceWindow.eid = localEid; + state->resourceWindow.eidIndex = state->eidInfo.eidIndex; + state->resourceWindow.funcId = state->eidInfo.funcId; + state->resourceWindow.funcIdValid = true; + state->resourceWindow.raCtxRegistered = false; + + uint16_t jettyCtxId = 0; + if (!SelectEndpointRouteJettyCtxId(state->eidInfo.funcId, peerOrdinal, &jettyCtxId)) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + ret = SelectTpRouteForPeer( + state->resourceWindow.raCtxHandle, + localEid, + peerEid, + state->tpType, + &state->localTpHandle, + &state->mappedJettyPriority); + if (ret != TILEXR_SUCCESS) { + ReleasePeerEndpointState(state); + return ret; + } + + 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; + ret = loader_.RaCtxCqCreate(state->resourceWindow.raCtxHandle, &cqInfo, &state->cqHandle); + if (ret != 0 || state->cqHandle == nullptr) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_MKIRT; + } + + const uint32_t sqDepth = SelectEndpointRouteSqDepth(); + TileXRCcuHccpQpCreateAttr qpAttr {}; + qpAttr.scqHandle = state->cqHandle; + qpAttr.rcqHandle = state->cqHandle; + qpAttr.srqHandle = state->cqHandle; + 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 = static_cast(TILEXR_CCU_DIRECT_LOOP_JETTY_ID + jettyCtxId); + qpAttr.ub.tokenIdHandle = state->resourceWindow.tokenIdHandle; + qpAttr.ub.tokenValue = state->jettyTokenValue; + qpAttr.ub.flag.value = 0; + qpAttr.ub.flag.bs.shareJfr = 1; + qpAttr.ub.jfsFlag.bs.errorSuspend = 1; + qpAttr.ub.priority = state->mappedJettyPriority; + qpAttr.ub.rnrRetry = TILEXR_CCU_HCCP_RNR_RETRY_DEFAULT; + qpAttr.ub.errTimeout = state->tpType == TILEXR_CCU_HCCP_TP_TYPE_CTP ? + TILEXR_CCU_DIRECT_CTP_ENDPOINT_ERR_TIMEOUT : TILEXR_CCU_DIRECT_ENDPOINT_ERR_TIMEOUT; + qpAttr.ub.extMode.cstmFlag.value = 0; + qpAttr.ub.extMode.cstmFlag.bs.sqCstm = 1; + qpAttr.ub.extMode.sq.buffVa = SelectEndpointRouteSqVa(localResourceWindow_, jettyCtxId); + qpAttr.ub.extMode.sq.buffSize = SelectEndpointRouteSqBytes(sqDepth); + qpAttr.ub.extMode.sqebbNum = sqDepth; + ret = loader_.RaCtxQpCreate( + state->resourceWindow.raCtxHandle, + &qpAttr, + &state->qpInfo, + &state->qpHandle); + if (ret != 0 || state->qpHandle == nullptr || state->qpInfo.key.size == 0) { + ReleasePeerEndpointState(state); + return TILEXR_ERROR_MKIRT; + } + state->psn = endpointPsn_++; + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace peerEndpoint created" + << " rank=" << options_.rank + << " peerRank=" << peerRank + << " peerDevice=" << peerDevicePhyId + << " localEid=" << FormatEndpointEid(localEid) + << " eidIndex=" << state->eidInfo.eidIndex + << " funcId=" << state->eidInfo.funcId + << " tpHandle=0x" << std::hex << state->localTpHandle << std::dec + << " priority=" << static_cast(state->mappedJettyPriority) + << " tpType=" << state->tpType + << " jettyCtxId=" << jettyCtxId + << " sqVa=0x" << std::hex << qpAttr.ub.extMode.sq.buffVa << std::dec + << " qpId=" << state->qpInfo.ub.id + << " psn=" << state->psn + << std::endl; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::PreparePeerEndpointRoutes(TileXRCcuDirectRuntimeReport* report) +{ + ReleasePeerEndpointRoutes(); + std::vector allDevicePhyIds(static_cast(options_.rankSize), 0); + int ret = options_.allGather( + &devicePhyId_, + sizeof(devicePhyId_), + allDevicePhyIds.data(), + options_.allGatherUserData); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "failed to exchange physical device ids for CCU endpoint routes", ret); + } + std::vector peerDevicePhyIds; + std::vector peerRanks; + for (int peer = 0; peer < options_.rankSize; ++peer) { + if (peer != options_.rank) { + peerRanks.push_back(static_cast(peer)); + peerDevicePhyIds.push_back(allDevicePhyIds[static_cast(peer)]); + } + } + std::vector topologyRoutes; + std::string topologyMessage; + ret = TileXRCcuResolvePeerEidRoutes( + TILEXR_CCU_HCCL_ROOT_INFO_PATH, + devicePhyId_, + peerDevicePhyIds, + &topologyRoutes, + &topologyMessage); + if (ret != TILEXR_SUCCESS || topologyRoutes.size() != peerRanks.size()) { + return Fail(report, topologyMessage.empty() ? + "failed to resolve peer-specific CCU EIDs" : topologyMessage, ret); + } + + std::vector> localEidsByPeer( + static_cast(options_.rankSize)); + for (uint32_t ordinal = 0; ordinal < peerRanks.size(); ++ordinal) { + localEidsByPeer[peerRanks[ordinal]] = topologyRoutes[ordinal].localEid; + } + std::vector> allLocalEidsByPeer( + static_cast(options_.rankSize) * static_cast(options_.rankSize)); + ret = options_.allGather( + localEidsByPeer.data(), + localEidsByPeer.size() * sizeof(localEidsByPeer.front()), + allLocalEidsByPeer.data(), + options_.allGatherUserData); + if (ret != TILEXR_SUCCESS) { + return Fail(report, "failed to exchange peer-specific CCU topology EIDs", ret); + } + + peerEndpointStates_.reserve(peerRanks.size()); + for (uint32_t ordinal = 0; ordinal < peerRanks.size(); ++ordinal) { + const auto& peerEid = allLocalEidsByPeer[ + static_cast(peerRanks[ordinal]) * static_cast(options_.rankSize) + + static_cast(options_.rank)]; + if (IsEmptyEid(peerEid)) { + ReleasePeerEndpointRoutes(); + return Fail(report, "missing reciprocal peer-specific CCU topology EID", TILEXR_ERROR_NOT_FOUND); + } + TileXRCcuPeerEndpointState state; + ret = CreatePeerEndpointState( + peerRanks[ordinal], + peerDevicePhyIds[ordinal], + topologyRoutes[ordinal].localEid, + peerEid, + topologyRoutes[ordinal].tpType, + ordinal, + &state); + if (ret != TILEXR_SUCCESS) { + ReleasePeerEndpointRoutes(); + return Fail(report, "failed to create peer-specific CCU endpoint", ret); + } + peerEndpointStates_.push_back(state); + } + + std::vector localOffers(static_cast(options_.rankSize)); + for (const auto& state : peerEndpointStates_) { + auto& offer = localOffers[state.peerRank]; + offer.resourceAddr = state.resourceWindow.addr; + offer.resourceTokenId = state.resourceWindow.tokenId; + offer.resourceRawTokenId = state.resourceWindow.rawTokenId; + offer.resourceTokenValue = state.resourceWindow.tokenValue; + offer.jettyTokenValue = state.jettyTokenValue; + offer.eid = state.resourceWindow.eid; + offer.qpKey = state.qpInfo.key; + if (offer.qpKey.size == 0 || offer.qpKey.size > TILEXR_CCU_HCCP_QP_KEY_BYTES) { + ReleasePeerEndpointRoutes(); + return Fail(report, "peer-specific CCU QP key has an invalid size", TILEXR_ERROR_MKIRT); + } + offer.psn = state.psn; + offer.funcId = state.resourceWindow.funcId; + offer.funcIdValid = state.resourceWindow.funcIdValid; + offer.valid = true; + } + std::vector allOffers( + static_cast(options_.rankSize) * static_cast(options_.rankSize)); + ret = options_.allGather( + localOffers.data(), + localOffers.size() * sizeof(TileXRCcuPeerEndpointOffer), + allOffers.data(), + options_.allGatherUserData); + if (ret != TILEXR_SUCCESS) { + ReleasePeerEndpointRoutes(); + return Fail(report, "failed to exchange peer-specific CCU endpoint offers", ret); + } + + std::vector localTpHandles(static_cast(options_.rankSize), 0); + for (auto& state : peerEndpointStates_) { + const auto& peerOffer = allOffers[ + static_cast(state.peerRank) * static_cast(options_.rankSize) + + static_cast(options_.rank)]; + if (!peerOffer.valid || peerOffer.qpKey.size == 0) { + ReleasePeerEndpointRoutes(); + return Fail(report, "missing reciprocal peer-specific CCU endpoint offer", TILEXR_ERROR_NOT_FOUND); + } + localTpHandles[state.peerRank] = state.localTpHandle; + } + std::vector allTpHandles( + static_cast(options_.rankSize) * static_cast(options_.rankSize), 0); + ret = options_.allGather( + localTpHandles.data(), + localTpHandles.size() * sizeof(uint64_t), + allTpHandles.data(), + options_.allGatherUserData); + if (ret != TILEXR_SUCCESS) { + ReleasePeerEndpointRoutes(); + return Fail(report, "failed to exchange peer-specific CCU TP handles", ret); + } + + for (uint32_t ordinal = 0; ordinal < peerEndpointStates_.size(); ++ordinal) { + auto& state = peerEndpointStates_[ordinal]; + const auto& peerOffer = allOffers[ + static_cast(state.peerRank) * static_cast(options_.rankSize) + + static_cast(options_.rank)]; + const uint64_t localTpHandle = localTpHandles[state.peerRank]; + const uint64_t peerTpHandle = allTpHandles[ + static_cast(state.peerRank) * static_cast(options_.rankSize) + + static_cast(options_.rank)]; + TileXRCcuHccpQpImportInfo importInfo {}; + importInfo.in.key = peerOffer.qpKey; + if (importInfo.in.key.size == 0 || importInfo.in.key.size > TILEXR_CCU_HCCP_QP_KEY_BYTES) { + ReleasePeerEndpointRoutes(); + return Fail(report, "peer-specific remote CCU QP key has an invalid size", TILEXR_ERROR_MKIRT); + } + importInfo.in.ub.mode = TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP; + importInfo.in.ub.tokenValue = peerOffer.jettyTokenValue; + 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 = state.psn; + importInfo.in.ub.expImportCfg.rxPsn = peerOffer.psn; + importInfo.in.ub.tpType = state.tpType; + ret = loader_.RaCtxQpImport( + state.resourceWindow.raCtxHandle, + &importInfo, + &state.remoteQpHandle); + if (ret != 0 || state.remoteQpHandle == nullptr) { + ReleasePeerEndpointRoutes(); + return Fail(report, "failed to import peer-specific CCU QP", TILEXR_ERROR_MKIRT); + } + state.route.remoteEid = ReverseEndpointEid(peerOffer.eid); + state.route.tpn = importInfo.out.ub.tpn; + state.route.doorbellVa = state.qpInfo.ub.dbAddr; + state.route.doorbellTokenId = + state.qpInfo.ub.dbTokenId >> TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT; + state.route.doorbellTokenValue = state.jettyTokenValue; + state.route.sqDepth = SelectEndpointRouteSqDepth(); + state.route.startJettyId = static_cast(state.qpInfo.ub.id); + state.route.remoteCcuVa = peerOffer.resourceAddr; + state.route.memoryTokenId = peerOffer.resourceTokenId; + state.route.memoryTokenValue = peerOffer.resourceTokenValue; + state.route.endpointRouteVerified = true; + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace peerEndpoint imported" + << " rank=" << options_.rank + << " peerRank=" << state.peerRank + << " localEid=" << FormatEndpointEid(state.resourceWindow.eid) + << " peerEid=" << FormatEndpointEid(peerOffer.eid) + << " localTpHandle=0x" << std::hex << localTpHandle + << " peerTpHandle=0x" << peerTpHandle + << std::dec + << " localMemoryTokenId=0x" << std::hex << state.resourceWindow.tokenId + << " peerMemoryTokenId=0x" << peerOffer.resourceTokenId + << " localCcuResourceTokenId=0x" << state.resourceWindow.tokenId + << std::dec + << " localPsn=" << state.psn + << " peerPsn=" << peerOffer.psn + << " tpn=0x" << std::hex << state.route.tpn + << " doorbellVa=0x" << state.route.doorbellVa + << " remoteCcuVa=0x" << state.route.remoteCcuVa + << std::dec + << " taJettyId=" << state.route.startJettyId + << std::endl; + } + } + localVerifiedEndpointRoute_ = peerEndpointStates_.front().route; + localVerifiedEndpointRouteValid_ = true; + if (report != nullptr) { + report->initialized = initialized_; + report->message = "peer-specific direct CCU endpoint routes prepared"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::QueryTpHandleForPeer( + const std::array& peerEid, + uint64_t* tpHandle) +{ + return QueryTpHandleForPeer( + localResourceWindow_.raCtxHandle, + localResourceWindow_.eid, + peerEid, + tpHandle); +} + +int TileXRCcuDirectRuntime::SelectTpRouteForPeer( + void* ctxHandle, + const std::array& localEid, + const std::array& peerEid, + uint32_t tpType, + uint64_t* tpHandle, + uint8_t* mappedJettyPriority) +{ + if (ctxHandle == nullptr || tpHandle == nullptr || mappedJettyPriority == nullptr || + IsEmptyEid(localEid) || IsEmptyEid(peerEid) || + loader_.RaGetTpInfoListAsync == nullptr || loader_.RaGetTpAttrAsync == nullptr || + loader_.RaSetTpAttrAsync == nullptr || loader_.RaGetAsyncReqResult == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + *tpHandle = 0; + *mappedJettyPriority = 0; + + TileXRCcuHccpGetTpCfg tpCfg {}; + tpCfg.flag.bs.rtp = tpType == TILEXR_CCU_HCCP_TP_TYPE_RTP ? 1 : 0; + tpCfg.flag.bs.ctp = tpType == TILEXR_CCU_HCCP_TP_TYPE_CTP ? 1 : 0; + tpCfg.transMode = TILEXR_CCU_HCCP_TRANSPORT_MODE_RM; + std::copy(localEid.begin(), localEid.end(), tpCfg.localEid.raw); + std::copy(peerEid.begin(), peerEid.end(), tpCfg.peerEid.raw); + + std::array tpInfos {}; + uint32_t tpInfoNum = static_cast(tpInfos.size()); + void* reqHandle = nullptr; + int ret = loader_.RaGetTpInfoListAsync( + ctxHandle, &tpCfg, tpInfos.data(), &tpInfoNum, &reqHandle); + if (ret != 0 || reqHandle == nullptr) { + return TILEXR_ERROR_MKIRT; + } + ret = WaitRaCtxAsyncRequest(loader_, reqHandle); + if (ret != TILEXR_SUCCESS || tpInfoNum == 0 || tpInfoNum > tpInfos.size()) { + return ret == TILEXR_SUCCESS ? TILEXR_ERROR_NOT_FOUND : ret; + } + + TileXRCcuHccpTpAttr attr {}; + uint32_t attrBitmap = TILEXR_CCU_TP_ATTR_BITMAP_SL_AVAILABLE | + TILEXR_CCU_TP_ATTR_BITMAP_SL; + reqHandle = nullptr; + ret = loader_.RaGetTpAttrAsync( + ctxHandle, tpInfos[0].tpHandle, &attrBitmap, &attr, &reqHandle); + if (ret != 0 || reqHandle == nullptr) { + return TILEXR_ERROR_MKIRT; + } + ret = WaitRaCtxAsyncRequest(loader_, reqHandle); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + uint32_t tpIndex = 0; + uint8_t mappedSl = 0; + if (!MapQosToTpAndSl( + TILEXR_CCU_DEFAULT_HCCL_QOS, tpInfoNum, attr.slBitmap, &tpIndex, &mappedSl) || + tpInfos[tpIndex].tpHandle == 0) { + return TILEXR_ERROR_NOT_FOUND; + } + + if (tpType == TILEXR_CCU_HCCP_TP_TYPE_RTP) { + TileXRCcuHccpTpAttr setAttr {}; + setAttr.sl = mappedSl; + reqHandle = nullptr; + ret = loader_.RaSetTpAttrAsync( + ctxHandle, + tpInfos[tpIndex].tpHandle, + TILEXR_CCU_TP_ATTR_BITMAP_SL, + &setAttr, + &reqHandle); + if (ret != 0 || reqHandle == nullptr) { + return TILEXR_ERROR_MKIRT; + } + ret = WaitRaCtxAsyncRequest(loader_, reqHandle); + if (ret != TILEXR_SUCCESS) { + return ret; + } + } + + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute selectedTp" + << " localEid=" << FormatEndpointEid(localEid) + << " peerEid=" << FormatEndpointEid(peerEid) + << " tpCount=" << tpInfoNum + << " slBitmap=0x" << std::hex << attr.slBitmap + << " tpIndex=" << std::dec << tpIndex + << " tpHandle=0x" << std::hex << tpInfos[tpIndex].tpHandle + << std::dec << " mappedSl=" << static_cast(mappedSl) + << std::endl; + } + *tpHandle = tpInfos[tpIndex].tpHandle; + *mappedJettyPriority = mappedSl; + return TILEXR_SUCCESS; +} + +int TileXRCcuDirectRuntime::QueryTpHandleForPeer( + void* ctxHandle, + const std::array& localEid, + const std::array& peerEid, + uint64_t* tpHandle) +{ + if (tpHandle == nullptr || IsEmptyEid(localEid) || IsEmptyEid(peerEid) || + ctxHandle == 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] = localEid[i]; + tpCfg.peerEid.raw[i] = peerEid[i]; + } + + TileXRCcuHccpTpInfo tpInfo {}; + uint32_t tpInfoNum = 1; + void* reqHandle = nullptr; + const int ret = loader_.RaGetTpInfoListAsync( + ctxHandle, + &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; + } + if (TraceEndpointRoute()) { + std::cerr << "TileXRDirectCcuTrace endpointRoute peerTpInfoReady" + << " localEid=" << FormatEndpointEid(localEid) + << " peerEid=" << FormatEndpointEid(peerEid) + << " tpHandle=0x" << std::hex << tpInfo.tpHandle + << std::dec << std::endl; + } + *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->startJettyId = localVerifiedEndpointRoute_.startJettyId; + 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 (!peerEndpointStates_.empty()) { + buffers->reserve(peerEndpointStates_.size()); + for (const auto& state : peerEndpointStates_) { + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = state.route.remoteCcuVa; + remote.peerRank = state.peerRank; + remote.memoryTokenId = state.route.memoryTokenId; + remote.rawMemoryTokenId = state.route.memoryTokenId << TILEXR_CCU_URMA_TOKEN_ID_RIGHT_SHIFT; + remote.memoryTokenValue = state.route.memoryTokenValue; + remote.localPfeId = state.resourceWindow.funcId; + remote.localPfeIdValid = state.resourceWindow.funcIdValid; + remote.remoteEid = state.route.remoteEid; + remote.tpn = state.route.tpn; + remote.doorbellVa = state.route.doorbellVa; + remote.doorbellTokenId = state.route.doorbellTokenId; + remote.doorbellTokenValue = state.route.doorbellTokenValue; + remote.sqDepth = state.route.sqDepth; + remote.startJettyId = state.route.startJettyId; + remote.localDoorbellVa = state.route.doorbellVa; + remote.localDoorbellTokenId = state.route.doorbellTokenId; + remote.localDoorbellTokenValue = state.route.doorbellTokenValue; + remote.localSqDepth = state.route.sqDepth; + remote.endpointRouteVerified = true; + buffers->push_back(remote); + } + return TILEXR_SUCCESS; + } + 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, + }; + local.funcId = localResourceWindow_.funcId; + local.funcIdValid = localResourceWindow_.funcIdValid; + 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.startJettyId = localVerifiedEndpointRoute_.startJettyId; + 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.localPfeId = localResourceWindow_.funcId; + remote.localPfeIdValid = localResourceWindow_.funcIdValid; + 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; + remote.startJettyId = importedRoute.startJettyId; + } else { + remote.tpn = peerWindow.tpn; + remote.doorbellVa = peerWindow.doorbellVa; + remote.doorbellTokenId = peerWindow.doorbellTokenId; + remote.doorbellTokenValue = peerWindow.doorbellTokenValue; + remote.sqDepth = peerWindow.sqDepth; + remote.startJettyId = peerWindow.startJettyId; + 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 + << " startJettyId=0x" << remote.startJettyId + << " memoryTokenId=0x" << remote.memoryTokenId + << " rawMemoryTokenId=0x" << remote.rawMemoryTokenId + << " memoryTokenValue=0x" << remote.memoryTokenValue + << std::dec + << " localPfeId=" << remote.localPfeId + << " localPfeIdValid=" << (remote.localPfeIdValid ? 1 : 0) + << " 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 00000000..d016c740 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_direct_runtime.h @@ -0,0 +1,241 @@ +/* + * 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; + uint32_t funcId = 0; + bool funcIdValid = false; + 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; + uint16_t startJettyId = 0; + uint32_t funcId = 0; + bool funcIdValid = false; + TileXRCcuHccpQpKey qpKey {}; + uint32_t psn = 0; + bool endpointRouteVerified = false; + bool channelResourceOwnerVerified = false; + bool transportResourceExchangeVerified = false; +}; + +struct TileXRCcuRegisteredMemoryBufferInfo { + uint64_t addr = 0; + uint64_t bytes = 0; + uint64_t alignedAddr = 0; + uint64_t alignedBytes = 0; + uint64_t targetSegVa = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; + TileXRCcuHccpMemKey key {}; + void* tokenIdHandle = nullptr; + void* lmemHandle = nullptr; + bool valid = false; +}; + +struct TileXRCcuRemoteMemoryBufferImportRequest { + uint64_t addr = 0; + uint64_t bytes = 0; + uint64_t alignedAddr = 0; + uint64_t offset = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; + TileXRCcuHccpMemKey key {}; + bool valid = false; +}; + +struct TileXRCcuImportedRemoteMemoryBufferInfo { + uint64_t addr = 0; + uint64_t bytes = 0; + uint64_t targetSegVa = 0; + void* rmemHandle = nullptr; + bool valid = 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; +}; + +struct TileXRCcuPeerEndpointState { + uint32_t peerRank = 0; + uint32_t peerDevicePhyId = 0; + TileXRCcuLocalResourceWindowInfo resourceWindow; + TileXRCcuHccpDevEidInfo eidInfo {}; + void* cqHandle = nullptr; + void* qpHandle = nullptr; + void* remoteQpHandle = nullptr; + TileXRCcuHccpQpCreateInfo qpInfo {}; + TileXRCcuLowerLayerTransportRoute route; + uint32_t jettyTokenValue = 0; + uint32_t tpType = TILEXR_CCU_HCCP_TP_TYPE_RTP; + uint32_t psn = 0; + uint64_t localTpHandle = 0; + uint8_t mappedJettyPriority = 0; +}; + +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 RegisterMemoryBuffer(uint64_t addr, uint64_t bytes, TileXRCcuRegisteredMemoryBufferInfo* info); + int ImportRemoteMemoryBuffer( + const TileXRCcuRemoteMemoryBufferImportRequest& request, + TileXRCcuImportedRemoteMemoryBufferInfo* info); + 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 PreparePeerEndpointRoutes(TileXRCcuDirectRuntimeReport* report); + int CreatePeerEndpointState( + uint32_t peerRank, + uint32_t peerDevicePhyId, + const std::array& localEid, + const std::array& peerEid, + uint32_t tpType, + uint32_t peerOrdinal, + TileXRCcuPeerEndpointState* state); + int SelectTpRouteForPeer( + void* ctxHandle, + const std::array& localEid, + const std::array& peerEid, + uint32_t tpType, + uint64_t* tpHandle, + uint8_t* mappedJettyPriority); + int QueryTpHandleForPeer( + const std::array& peerEid, + uint64_t* tpHandle); + int QueryTpHandleForPeer( + void* ctxHandle, + const std::array& localEid, + 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 ReleaseImportedRemoteMemoryBuffers(); + void ReleaseRegisteredMemoryBuffers(); + void ReleaseRegisteredResourceWindow(); + void ReleaseLocalEndpointRoute(); + void ReleasePeerEndpointState(TileXRCcuPeerEndpointState* state); + void ReleasePeerEndpointRoutes(); + + 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_; + std::vector peerEndpointStates_; + std::vector registeredMemoryBuffers_; + std::vector importedRemoteMemoryBuffers_; + 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 00000000..9d507b64 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.cpp @@ -0,0 +1,636 @@ +/* + * 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::ReadMissionContext( + uint8_t dieId, + uint8_t missionId, + void* missionContext, + uint32_t missionContextBytes, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (missionContext == nullptr || missionContextBytes == 0 || + missionContextBytes > TILEXR_CCU_DATA_ARRAY_SLOT_BYTES) { + return Fail(report, "invalid CCU mission context readback buffer"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_GET_MISSION_CTX, &in); + in.offsetStartIdx = missionId; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = missionContextBytes; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_GET_MISSION_CTX, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + std::memcpy(missionContext, &out.data.dataInfo.dataArray[0], missionContextBytes); + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::ReadResourceRange( + uint8_t dieId, + uint32_t opcode, + uint32_t startId, + uint64_t* values, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (values == nullptr || count == 0) { + return Fail(report, "invalid CCU resource readback range"); + } + + uint32_t remaining = count; + uint32_t offset = startId; + uint32_t outputOffset = 0; + while (remaining > 0) { + const uint32_t batch = std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, opcode, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * sizeof(uint64_t); + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, opcode, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + for (uint32_t i = 0; i < batch; ++i) { + std::memcpy(&values[outputOffset + i], &out.data.dataInfo.dataArray[i], sizeof(uint64_t)); + } + remaining -= batch; + offset += batch; + outputOffset += batch; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::ReadXnRange( + uint8_t dieId, + uint32_t startXnId, + uint64_t* values, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + return ReadResourceRange(dieId, TILEXR_CCU_U_OP_GET_XN, startXnId, values, count, report); +} + +int TileXRCcuDriverAdapter::ReadCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint64_t* values, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + return ReadResourceRange(dieId, TILEXR_CCU_U_OP_GET_CKE, startCkeId, values, count, report); +} + +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::SetTaskKill(uint8_t dieId, TileXRCcuDriverAdapterReport* report) const +{ + TileXRCcuCustomChannelOut out; + return Call(dieId, TILEXR_CCU_U_OP_SET_TASKKILL, &out, report); +} + +int TileXRCcuDriverAdapter::CleanTaskKillState(uint8_t dieId, TileXRCcuDriverAdapterReport* report) const +{ + TileXRCcuCustomChannelOut out; + return Call(dieId, TILEXR_CCU_U_OP_CLEAN_TASKKILL_STATE, &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) { + return Fail(report, "invalid CCU local jetty context count"); + } + + uint32_t remaining = count; + uint32_t offset = startJettyCtxId; + uint32_t inputOffset = 0; + 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_JETTY_CTX, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES; + for (uint32_t i = 0; i < batch; ++i) { + CopyPayloadToSlot(ctxs[inputOffset + i], &in.data.dataInfo.dataArray[i]); + } + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_SET_JETTY_CTX, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + remaining -= batch; + offset += batch; + inputOffset += batch; + } + return TILEXR_SUCCESS; +} + +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 00000000..b2c9a9e5 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.h @@ -0,0 +1,179 @@ +/* + * 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_GET_XN = 203; +constexpr uint32_t TILEXR_CCU_U_OP_GET_CKE = 204; +constexpr uint32_t TILEXR_CCU_U_OP_GET_MISSION_CTX = 208; +constexpr uint32_t TILEXR_CCU_U_OP_SET_MSID_TOKEN = 53; +constexpr uint32_t TILEXR_CCU_U_OP_SET_TASKKILL = 54; +constexpr uint32_t TILEXR_CCU_U_OP_CLEAN_TASKKILL_STATE = 55; +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 ReadMissionContext( + uint8_t dieId, + uint8_t missionId, + void* missionContext, + uint32_t missionContextBytes, + TileXRCcuDriverAdapterReport* report) const; + int ReadXnRange( + uint8_t dieId, + uint32_t startXnId, + uint64_t* values, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + int ReadCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint64_t* values, + uint32_t count, + 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 SetTaskKill(uint8_t dieId, TileXRCcuDriverAdapterReport* report) const; + int CleanTaskKillState(uint8_t dieId, 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; + int ReadResourceRange( + uint8_t dieId, + uint32_t opcode, + uint32_t startId, + uint64_t* values, + uint32_t count, + 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_executor.cpp b/src/comm/ccu/tilexr_ccu_executor.cpp new file mode 100644 index 00000000..fe463881 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_executor.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#include "ccu/tilexr_ccu_executor.h" + +#include "ccu/tilexr_ccu_runtime_session.h" + +namespace TileXR { + +int TileXRCcuExecutor::Submit( + const TileXRCcuRuntimeSession &session, + const TileXRCcuCollectivePlan &plan, + aclrtStream) const +{ + if (!session.Available()) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + return plan.ready ? TILEXR_ERROR_NOT_SUPPORT : TILEXR_ERROR_PARA_CHECK_FAIL; +} + +#ifdef TILEXR_CCU_TESTING +int TileXRCcuExecutor::ReadDirectCcuInstructionsForDebug( + TileXRCcuRuntimeSession &session, + uint8_t dieId, + uint16_t instructionStartId, + void *instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport *report) const +{ + if (report != nullptr) { + *report = TileXRCcuDriverAdapterReport{}; + } + if (!session.Available()) { + if (report != nullptr) { + report->message = "TileXRCcuBackend is not initialized for direct CCU instruction readback"; + } + return TILEXR_ERROR_NOT_INITIALIZED; + } + + TileXRCcuDriverAdapter adapter; + int ret = session.CreateDriverAdapter(&adapter, report); + if (ret != TILEXR_SUCCESS) { + if (report != nullptr && report->message.empty()) { + report->message = "direct CCU runtime is unavailable for instruction readback"; + } + return ret; + } + return adapter.ReadInstructions(dieId, instructionStartId, instructions, instructionCount, instructionBytes, report); +} +#endif + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_executor.h b/src/comm/ccu/tilexr_ccu_executor.h new file mode 100644 index 00000000..100f594d --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_executor.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#ifndef TILEXR_CCU_EXECUTOR_H +#define TILEXR_CCU_EXECUTOR_H + +#include "acl/acl_base.h" +#include "ccu/tilexr_ccu_backend.h" +#ifdef TILEXR_CCU_TESTING +#include "ccu/tilexr_ccu_driver_adapter.h" +#endif + +namespace TileXR { + +class TileXRCcuRuntimeSession; + +class TileXRCcuExecutor { +public: + int Submit(const TileXRCcuRuntimeSession &session, const TileXRCcuCollectivePlan &plan, aclrtStream stream) const; +#ifdef TILEXR_CCU_TESTING + int ReadDirectCcuInstructionsForDebug( + TileXRCcuRuntimeSession &session, + uint8_t dieId, + uint16_t instructionStartId, + void *instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport *report) const; +#endif +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_EXECUTOR_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 00000000..6d788ce1 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.cpp @@ -0,0 +1,589 @@ +/* + * 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_, RaCtxRmemImport, "RaCtxRmemImport", "ra_ctx_rmem_import"); + LoadOptionalSymbol(raHandle_, RaCtxRmemUnimport, "RaCtxRmemUnimport", "ra_ctx_rmem_unimport"); + 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_, RaGetTpAttrAsync, "RaGetTpAttrAsync", "ra_get_tp_attr_async"); + LoadOptionalSymbol(raHandle_, RaSetTpAttrAsync, "RaSetTpAttrAsync", "ra_set_tp_attr_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; + RaCtxRmemImport = nullptr; + RaCtxRmemUnimport = 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; + RaGetTpAttrAsync = nullptr; + RaSetTpAttrAsync = 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 00000000..42cb5f57 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.h @@ -0,0 +1,113 @@ +/* + * 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; + TileXRCcuRaCtxRmemImportFunc RaCtxRmemImport = nullptr; + TileXRCcuRaCtxRmemUnimportFunc RaCtxRmemUnimport = 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; + TileXRCcuRaGetTpAttrAsyncFunc RaGetTpAttrAsync = nullptr; + TileXRCcuRaSetTpAttrAsyncFunc RaSetTpAttrAsync = 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 00000000..9cb9b918 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_types.h @@ -0,0 +1,664 @@ +/* + * 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 TileXRCcuHccpImportSegFlag { + struct { + uint32_t cacheable : 1; + uint32_t access : 6; + uint32_t mapping : 1; + uint32_t reserved : 24; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpMemImportAttr { + TileXRCcuHccpMemKey key; + union { + struct { + TileXRCcuHccpImportSegFlag flags; + uint64_t mappingAddr; + uint32_t tokenValue; + } ub; + }; + uint32_t resv[4]; +}; + +struct TileXRCcuHccpMemImportInfo { + union { + struct { + uint64_t targetSegHandle; + } ub; + }; + uint32_t resv[4]; +}; + +struct TileXRCcuHccpMrImportInfo { + TileXRCcuHccpMemImportAttr in; + TileXRCcuHccpMemImportInfo 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; +}; + +#pragma pack(push, 1) +struct TileXRCcuHccpTpAttr { + uint8_t retryTimesInit : 3; + uint8_t at : 5; + uint8_t sip[16]; + uint8_t dip[16]; + uint8_t sma[6]; + uint8_t dma[6]; + uint16_t vlanId : 12; + uint8_t vlanEn : 1; + uint8_t dscp : 6; + uint8_t atTimes : 5; + uint8_t sl : 4; + uint8_t ttl; + uint16_t ackUdpSrcport; + uint16_t dataUdpSrcport; + uint8_t udpSrcportRange : 4; + uint8_t sprayEn : 1; + uint8_t udpGlobalEn : 1; + uint8_t reserve0 : 2; + uint16_t slBitmap; + uint8_t dscpConfigMode : 1; + uint8_t reserve1 : 7; + uint8_t reserved[70]; +}; +#pragma pack(pop) + +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 TileXRCcuRaCtxRmemImportFunc = int (*)( + void* ctx, + TileXRCcuHccpMrImportInfo* mr, + void** rmemHandle); +using TileXRCcuRaCtxRmemUnimportFunc = int (*)(void* ctx, void* rmemHandle); +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 TileXRCcuRaGetTpAttrAsyncFunc = int (*)( + void* ctx, + uint64_t tpHandle, + uint32_t* attrBitmap, + TileXRCcuHccpTpAttr* attr, + void** reqHandle); +using TileXRCcuRaSetTpAttrAsyncFunc = int (*)( + void* ctx, + uint64_t tpHandle, + uint32_t attrBitmap, + TileXRCcuHccpTpAttr* attr, + 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 00000000..f0adbe71 --- /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 00000000..5d12b51f --- /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 00000000..fa1a5ec0 --- /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 00000000..891b4d4e --- /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 00000000..8256d6b4 --- /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 00000000..30fb33fa --- /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 00000000..03b8dc8d --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.cpp @@ -0,0 +1,794 @@ +/* + * 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 +#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_PER_DIE_JETTY_NUM = 128; +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; +} + +void NormalizeVerifiedEndpointRouteJettyWindow(TileXRCcuLowerLayerTransportSnapshot* snapshot) +{ + if (snapshot == nullptr || snapshot->routes.empty()) { + return; + } + + uint16_t minExplicitStart = 0; + uint32_t explicitEnd = 0; + uint32_t explicitStartCount = 0; + for (const auto& route : snapshot->routes) { + if (route.startJettyId == 0) { + continue; + } + if (explicitStartCount == 0) { + minExplicitStart = route.startJettyId; + } else { + minExplicitStart = std::min(minExplicitStart, route.startJettyId); + } + explicitEnd = std::max(explicitEnd, static_cast(route.startJettyId) + 1U); + ++explicitStartCount; + } + + const bool configuredWindowContainsExplicitJettys = + snapshot->startJettyId != 0 && snapshot->pfeJettyCount != 0 && minExplicitStart != 0 && + minExplicitStart >= snapshot->startJettyId && + explicitEnd <= static_cast(snapshot->startJettyId) + snapshot->pfeJettyCount; + if (minExplicitStart != 0 && !configuredWindowContainsExplicitJettys) { + snapshot->startJettyId = minExplicitStart; + } + + uint32_t requiredJettyCount = static_cast(snapshot->routes.size()); + if (snapshot->startJettyId != 0) { + uint32_t end = static_cast(snapshot->startJettyId) + snapshot->routes.size(); + for (uint32_t i = 0; i < snapshot->routes.size(); ++i) { + const uint32_t start = snapshot->routes[i].startJettyId == 0 ? + static_cast(snapshot->startJettyId) + i : + snapshot->routes[i].startJettyId; + end = std::max(end, start + 1U); + } + requiredJettyCount = std::max( + requiredJettyCount, + end - static_cast(snapshot->startJettyId)); + } else if (explicitEnd != 0 && minExplicitStart != 0) { + requiredJettyCount = std::max( + requiredJettyCount, + explicitEnd - static_cast(minExplicitStart)); + } + snapshot->pfeJettyCount = CheckedU16(std::max(snapshot->pfeJettyCount, requiredJettyCount)); +} + +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_PER_DIE_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; + } + if (snapshot.remoteXnStartId != 0 && snapshot.remoteXnCount != 0) { + plan->xnClears.push_back({ + snapshot.dieId, + snapshot.remoteXnStartId, + snapshot.remoteXnCount, + }); + 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.jettys.front().startJettyCtxId == 0 ? + spec.pfe.startLocalJettyCtxId : + spec.jettys.front().startJettyCtxId; + 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.channels.num) { + 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 = remoteCcuBuffers.front().localPfeIdValid ? + remoteCcuBuffers.front().localPfeId : + 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.remoteXnStartId = allocation.remoteXn.startId; + result.remoteXnCount = allocation.remoteXn.num; + result.ckeStartId = localWaitCke.startId; + result.ckeCount = localWaitCke.num; + result.routes.reserve(remoteCcuBuffers.size()); + + const uint16_t wqeBasicBlockStride = SelectLowerLayerWqeBasicBlockStride(); + std::map wqeStartByJettyId; + uint32_t nextVerifiedWqeStartId = 0; + 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.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.startJettyId = remoteCcuBuffer.startJettyId; + route.endpointRouteVerified = true; + } + uint32_t wqeOrdinal = i; + if (route.startJettyId != 0) { + const uint32_t sqDepth = route.localSqDepth == 0 ? route.sqDepth : route.localSqDepth; + const uint32_t wqeBasicBlockCount = sqDepth * TILEXR_CCU_WQE_BASIC_BLOCKS_PER_ROUTE; + if (wqeBasicBlockCount == 0 || nextVerifiedWqeStartId > std::numeric_limits::max()) { + return Fail(nullptr, report, "invalid verified endpoint WQE basic block window"); + } + uint32_t wqeBasicBlockStartId = nextVerifiedWqeStartId; + const bool pfeWindowContainsJetty = result.pfeJettyCount != 0 && + route.startJettyId >= result.startJettyId && + static_cast(route.startJettyId) < + static_cast(result.startJettyId) + result.pfeJettyCount; + if (pfeWindowContainsJetty) { + const uint32_t localJettyOffset = + static_cast(route.startJettyId) - result.startJettyId; + wqeBasicBlockStartId = localJettyOffset * wqeBasicBlockCount; + } + if (wqeBasicBlockStartId > std::numeric_limits::max()) { + return Fail(nullptr, report, "verified endpoint WQE basic block start overflows"); + } + const auto inserted = wqeStartByJettyId.emplace( + route.startJettyId, + static_cast(wqeBasicBlockStartId)); + route.wqeBasicBlockStartId = inserted.first->second; + if (inserted.second && !pfeWindowContainsJetty) { + nextVerifiedWqeStartId += wqeBasicBlockCount; + } + } else { + if (wqeOrdinal > std::numeric_limits::max() / wqeBasicBlockStride) { + return Fail(nullptr, report, "lower-layer CCU WQE basic block start overflows"); + } + route.wqeBasicBlockStartId = static_cast(wqeOrdinal * wqeBasicBlockStride); + } + result.routes.push_back(route); + } + NormalizeVerifiedEndpointRouteJettyWindow(&result); + + *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->startJettyId = verified.startJettyId; + routeIt->endpointRouteVerified = true; + routeIt->channelResourceOwnerVerified = verified.channelResourceOwnerVerified; + routeIt->transportResourceExchangeVerified = verified.transportResourceExchangeVerified; + } + NormalizeVerifiedEndpointRouteJettyWindow(snapshot); + + 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"); + } + + TileXRCcuLowerLayerTransportSnapshot normalized = snapshot; + NormalizeVerifiedEndpointRouteJettyWindow(&normalized); + + TileXRCcuLowerLayerPlanSpec spec; + spec.msidToken = normalized.msidToken; + spec.pfe.dieId = normalized.dieId; + spec.pfe.pfeOffset = normalized.pfeOffset; + spec.pfe.startJettyId = normalized.startJettyId; + spec.pfe.jettyCount = normalized.pfeJettyCount; + spec.pfe.startLocalJettyCtxId = normalized.startLocalJettyCtxId; + spec.xnClear.dieId = normalized.dieId; + spec.xnClear.startXnId = normalized.xnStartId; + spec.xnClear.count = normalized.xnCount; + spec.xnClear.valid = normalized.xnCount != 0; + spec.ckeClear.dieId = normalized.dieId; + spec.ckeClear.startCkeId = normalized.ckeStartId; + spec.ckeClear.count = normalized.ckeCount; + spec.ckeClear.valid = normalized.ckeCount != 0; + + std::map jettyRoutes; + for (uint32_t i = 0; i < normalized.routes.size(); ++i) { + const auto& route = normalized.routes[i]; + const uint16_t jettyId = route.startJettyId == 0 ? + static_cast(normalized.startJettyId + i) : + route.startJettyId; + jettyRoutes.emplace(jettyId, &route); + } + for (const auto& entry : jettyRoutes) { + if (entry.first < normalized.startJettyId) { + return Fail(plan, report, "lower-layer CCU endpoint jetty ID precedes the PFE jetty window"); + } + const uint32_t localJettyOffset = static_cast(entry.first) - normalized.startJettyId; + if (localJettyOffset >= normalized.pfeJettyCount || + static_cast(normalized.startLocalJettyCtxId) + localJettyOffset >= 128U) { + return Fail(plan, report, "lower-layer CCU endpoint jetty ID is outside the PFE jetty window"); + } + const auto& route = *entry.second; + TileXRCcuLowerLayerJettySpec jetty; + jetty.dieId = normalized.dieId; + jetty.pfeId = normalized.pfeId; + jetty.startJettyCtxId = static_cast(normalized.startLocalJettyCtxId + localJettyOffset); + 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); + } + + uint32_t routeIndex = 0; + for (const auto& route : normalized.routes) { + TileXRCcuLowerLayerChannelSpec channel; + channel.dieId = normalized.dieId; + channel.channelId = route.channelId; + channel.remoteEid = route.remoteEid; + channel.tpn = route.tpn; + channel.sourcePfeId = normalized.pfeId; + channel.startJettyId = route.startJettyId == 0 ? + static_cast(normalized.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 = normalized.dieId; + remoteXn.channelId = static_cast(route.channelId); + remoteXn.localXn = static_cast(normalized.xnStartId + routeIndex); + remoteXn.remoteXn = route.remoteXnId; + remoteXn.notifyCke = route.remoteNotifyCke == 0 ? + static_cast(normalized.ckeStartId + routeIndex) : + route.remoteNotifyCke; + remoteXn.peerRank = route.peerRank; + remoteXn.peerExchangeObserved = route.remoteXnId != 0; + remoteXn.localWaitCke = static_cast(normalized.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(normalized, plan); + AppendRemoteNotifyCkeClears(normalized, 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 00000000..5a71bce9 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_lower_layer_plan_builder.h @@ -0,0 +1,187 @@ +/* + * 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; + uint16_t startJettyId = 0; + uint32_t localPfeId = 0; + bool localPfeIdValid = false; + 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 remoteXnStartId = 0; + uint32_t remoteXnCount = 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 00000000..a1559abf --- /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 00000000..19640b5e --- /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 00000000..1ba5e4e3 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_microcode.cpp @@ -0,0 +1,371 @@ +/* + * 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_LOC_MEM_TO_LOC_MS_HEADER = 0x1000U; +constexpr uint64_t TILEXR_CCU_TRANS_LOC_MS_TO_LOC_MEM_HEADER = 0x1002U; +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_TRANS_LOC_MEM_TO_LOC_MEM_HEADER = 0x100aU; +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; +} + +int ValidateLocalMsTransferSpec(const TileXRCcuLocalMsTransferSpec& spec) +{ + if (spec.localGsa == 0 || spec.localXn == 0 || spec.lengthXn == 0) { + 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; +} + +uint16_t LocalMsTransferFlagSlot(const TileXRCcuLocalMsTransferSpec& spec) +{ + return static_cast((spec.clearWait ? 1U : 0U) | (spec.lengthFromXn ? 2U : 0U)); +} + +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 TileXRCcuEncodeTransLocMemToLocMem(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_LOC_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 TileXRCcuEncodeTransLocMemToLocMs(const TileXRCcuLocalMsTransferSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS || + ValidateLocalMsTransferSpec(spec) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots( + static_cast(TILEXR_CCU_TRANS_LOC_MEM_TO_LOC_MS_HEADER), + spec.localMs, + spec.localGsa, + spec.localXn); + instr->words[1] = PackSlots(spec.lengthXn, spec.channelId, 0, 0); + instr->words[2] = PackSlots(0, 0, 0, LocalMsTransferFlagSlot(spec)); + instr->words[3] = PackSlots(spec.setCkeId, spec.setCkeMask, spec.waitCkeId, spec.waitCkeMask); + return TILEXR_SUCCESS; +} + +int TileXRCcuEncodeTransLocMsToLocMem(const TileXRCcuLocalMsTransferSpec& spec, TileXRCcuInstr* instr) +{ + if (ValidateInstrOutput(instr) != TILEXR_SUCCESS || + ValidateLocalMsTransferSpec(spec) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + + instr->words[0] = PackSlots( + static_cast(TILEXR_CCU_TRANS_LOC_MS_TO_LOC_MEM_HEADER), + spec.localGsa, + spec.localXn, + spec.localMs); + instr->words[1] = PackSlots(spec.lengthXn, spec.channelId, 0, 0); + instr->words[2] = PackSlots(0, 0, 0, LocalMsTransferFlagSlot(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 00000000..fbe59783 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_microcode.h @@ -0,0 +1,120 @@ +/* + * 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; +}; + +struct TileXRCcuLocalMsTransferSpec { + uint16_t localGsa = 0; + uint16_t localXn = 0; + uint16_t localMs = 0; + uint16_t lengthXn = 0; + uint16_t channelId = 0; + uint16_t setCkeId = 0; + uint16_t setCkeMask = 0; + uint16_t waitCkeId = 0; + uint16_t waitCkeMask = 0; + bool clearWait = true; + bool lengthFromXn = true; +}; + +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 TileXRCcuEncodeTransLocMemToLocMem(const TileXRCcuMemTransferSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeTransLocMemToLocMs(const TileXRCcuLocalMsTransferSpec& spec, TileXRCcuInstr* instr); + +int TileXRCcuEncodeTransLocMsToLocMem(const TileXRCcuLocalMsTransferSpec& 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 00000000..d87cd503 --- /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 00000000..af4403c2 --- /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 00000000..d42265b6 --- /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 00000000..2a96436f --- /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 00000000..b160ca62 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr int TILEXR_CCU_ROCE_EAGAIN = 128101; +constexpr uint32_t TILEXR_CCU_RA_EAGAIN_RETRY_COUNT = 100; +constexpr uint32_t TILEXR_CCU_RA_EAGAIN_RETRY_INTERVAL_MS = 100; + +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; + for (uint32_t retry = 0; retry <= TILEXR_CCU_RA_EAGAIN_RETRY_COUNT; ++retry) { + std::memset(out, 0, sizeof(*out)); + const int ret = provider->raCustomChannel_(info, in, out); + if (ret != TILEXR_CCU_ROCE_EAGAIN || retry == TILEXR_CCU_RA_EAGAIN_RETRY_COUNT) { + return ret; + } + std::this_thread::sleep_for(std::chrono::milliseconds(TILEXR_CCU_RA_EAGAIN_RETRY_INTERVAL_MS)); + } + return TILEXR_CCU_ROCE_EAGAIN; +} + +} // 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 00000000..48f9984d --- /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 00000000..4b9b6bcf --- /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 00000000..5a7fdc02 --- /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 00000000..a797a149 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_resource_allocator.cpp @@ -0,0 +1,421 @@ +/* + * 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; +constexpr uint32_t TILEXR_CCU_CHANNEL_XN_STRIDE = 8U; + +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 ? request.sourceCkeCount : 0U; + if (syncCkeMode && sourceCkeResourceCount == 0) { + return Fail(report, "invalid CCU source CKE resource request"); + } + 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( + std::max( + std::max(localSqeXnCount, request.syncResourceCount), + request.bindingsPerSyncResource), + request.minimumLocalXnCount); + const uint32_t remoteXnStride = request.bindingsPerSyncResource > 1U ? + TILEXR_CCU_CHANNEL_XN_STRIDE : 1U; + if (request.syncResourceCount > std::numeric_limits::max() / remoteXnStride) { + return Fail(report, "remote XN resource count overflows"); + } + const uint32_t remoteXnCount = std::max( + request.syncResourceCount * remoteXnStride, + request.minimumRemoteXnCount); + 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 * remoteXnStride); + 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; + } + const auto latest = active_.rbegin(); + if (latest == active_.rend() || latest->first != receiptId) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + const ActiveAllocation& active = it->second; + if (mission_.used < active.missionUsed || + repository_.used < active.repositoryUsed || + xn_.used < active.localXnUsed || + gsa_.used < active.localGsaUsed || + localWaitCke_.used < static_cast(active.localWaitCkeUsed + active.sourceCkeUsed) || + remoteNotifyCke_.used < active.remoteNotifyCkeUsed || + channel_.used < active.channelUsed || + (remoteXn_.count != 0 && remoteXn_.used < active.remoteXnUsed)) { + return TILEXR_ERROR_INTERNAL; + } + mission_.used = static_cast(mission_.used - active.missionUsed); + repository_.used = static_cast(repository_.used - active.repositoryUsed); + xn_.used = static_cast(xn_.used - active.localXnUsed); + if (remoteXn_.count == 0) { + xn_.used = static_cast(xn_.used - active.remoteXnUsed); + } else { + remoteXn_.used = static_cast(remoteXn_.used - active.remoteXnUsed); + } + gsa_.used = static_cast(gsa_.used - active.localGsaUsed); + localWaitCke_.used = static_cast( + localWaitCke_.used - active.localWaitCkeUsed - active.sourceCkeUsed); + remoteNotifyCke_.used = static_cast(remoteNotifyCke_.used - active.remoteNotifyCkeUsed); + channel_.used = static_cast(channel_.used - active.channelUsed); + 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 00000000..67e7e040 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_resource_allocator.h @@ -0,0 +1,133 @@ +/* + * 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; + uint32_t minimumLocalXnCount = 0; + uint32_t minimumRemoteXnCount = 0; + uint32_t sourceCkeCount = 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 00000000..138b7cbd --- /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 00000000..7d406990 --- /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_runtime_session.cpp b/src/comm/ccu/tilexr_ccu_runtime_session.cpp new file mode 100644 index 00000000..a9001a20 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_runtime_session.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#include "ccu/tilexr_ccu_runtime_session.h" + +#include "tilexr_log.h" +#include "tools/socket/tilexr_sock_exchange.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace chrono; + +namespace TileXR { + +constexpr int TILEXR_INIT_TIMEOUT = 600; + +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_ccuDirectRuntimeMtx; +static std::map g_ccuDirectRuntimeUnavailableByDevice; + +std::string TileXRCcuRuntimeSession::DirectCcuRuntimeUnavailableMessageForDevice(int devId) +{ + lock_guard lock(g_ccuDirectRuntimeMtx); + const auto it = g_ccuDirectRuntimeUnavailableByDevice.find(devId); + if (it == g_ccuDirectRuntimeUnavailableByDevice.end()) { + return {}; + } + return it->second.empty() ? + "direct CCU runtime unavailable for device " + std::to_string(devId) : + "direct CCU runtime unavailable for device " + std::to_string(devId) + ": " + it->second; +} + +std::string TileXRCcuRuntimeSession::DirectCcuRuntimeUnavailableMessage() const +{ + return DirectCcuRuntimeUnavailableMessageForDevice(devId_); +} + +void TileXRCcuRuntimeSession::Shutdown() +{ + initialized_ = false; + ResetDirectCcuBasicInfo(); + directCcuThreadAllGatherRound_ = 0; + if (ccuDirectRuntime_ != nullptr) { + ccuDirectRuntime_->Shutdown(); + ccuDirectRuntime_.reset(); + } + options_ = TileXRCcuBackendOptions {}; + socketExchange_ = nullptr; +} + +bool TileXRCcuRuntimeSession::Available() const +{ + return initialized_ && ccuDirectRuntime_ != nullptr && ccuDirectRuntime_->IsAvailable(); +} + +int TileXRCcuRuntimeSession::Rank() const +{ + return rank_; +} + +int TileXRCcuRuntimeSession::RankSize() const +{ + return rankSize_; +} + +int TileXRCcuRuntimeSession::DevId() const +{ + return devId_; +} + +int TileXRCcuRuntimeSession::Init(const TileXRCcuBackendOptions &options) +{ + Shutdown(); + options_ = options; + rank_ = options.rank; + rankSize_ = options.rankSize; + devId_ = options.devId; + uid_ = options.uid; + socketExchange_ = options.exchange; + if (rankSize_ <= 1) { + TILEXR_LOG(INFO) << "direct CCU runtime skipped for single-rank communicator"; + return TILEXR_SUCCESS; + } + + lock_guard lock(g_ccuDirectRuntimeMtx); + const auto unavailable = g_ccuDirectRuntimeUnavailableByDevice.find(devId_); + if (unavailable != g_ccuDirectRuntimeUnavailableByDevice.end()) { + TILEXR_LOG(INFO) << "direct CCU runtime skipped after previous init failure on device " << devId_; + return TILEXR_SUCCESS; + } + + ccuDirectRuntime_.reset(new (nothrow) TileXRCcuDirectRuntime()); + if (ccuDirectRuntime_ == nullptr) { + TILEXR_LOG(WARN) << "TileXRCcuDirectRuntime allocation failed, direct CCU disabled"; + return TILEXR_SUCCESS; + } + + TileXRCcuDirectRuntimeOptions runtimeOptions {}; + runtimeOptions.rank = rank_; + runtimeOptions.rankSize = rankSize_; + runtimeOptions.devId = devId_; + runtimeOptions.allGather = &TileXRCcuRuntimeSession::DirectCcuAllGatherCallback; + runtimeOptions.allGatherUserData = this; + TileXRCcuDirectRuntimeReport runtimeReport; + const int ret = ccuDirectRuntime_->Init(runtimeOptions, &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_ccuDirectRuntimeUnavailableByDevice[devId_] = 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); + initialized_ = true; + return TILEXR_SUCCESS; +} + +void TileXRCcuRuntimeSession::ResetDirectCcuBasicInfo() +{ + directCcuBasicInfoValid_ = false; + directCcuBasicInfoStatus_ = TILEXR_ERROR_NOT_FOUND; + directCcuBasicInfo_ = TileXRCcuBasicInfo {}; + directCcuBasicInfoReport_ = TileXRCcuDriverAdapterReport {}; +} + +int TileXRCcuRuntimeSession::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 TileXRCcuRuntimeSession::HasDirectCcuBasicInfo() const +{ + return directCcuBasicInfoValid_; +} + +int TileXRCcuRuntimeSession::GetDirectCcuBasicInfoStatus() const +{ + return directCcuBasicInfoStatus_; +} + +const TileXRCcuBasicInfo *TileXRCcuRuntimeSession::GetDirectCcuBasicInfo() const +{ + return directCcuBasicInfoValid_ ? &directCcuBasicInfo_ : nullptr; +} + +const TileXRCcuDriverAdapterReport &TileXRCcuRuntimeSession::GetDirectCcuBasicInfoReport() const +{ + return directCcuBasicInfoReport_; +} + +int TileXRCcuRuntimeSession::RegisterCcuResourceRmaBuffer(uint64_t resourceAddr) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->RegisterCcuResourceRmaBuffer(resourceAddr); +} + +int TileXRCcuRuntimeSession::RegisterMemoryBuffer( + uint64_t addr, + uint64_t bytes, + TileXRCcuRegisteredMemoryBufferInfo *info) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->RegisterMemoryBuffer(addr, bytes, info); +} + +int TileXRCcuRuntimeSession::ImportRemoteMemoryBuffer( + const TileXRCcuRemoteMemoryBufferImportRequest &request, + TileXRCcuImportedRemoteMemoryBufferInfo *info) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->ImportRemoteMemoryBuffer(request, info); +} + +int TileXRCcuRuntimeSession::ExportLocalCcuRmaBuffer(TileXRCcuLocalResourceWindowInfo *info) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->ExportLocalCcuRmaBuffer(info); +} + +int TileXRCcuRuntimeSession::ExportRemoteCcuRmaBuffers(std::vector *buffers) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->ExportRemoteCcuRmaBuffers(buffers); +} + +int TileXRCcuRuntimeSession::ExportLowerLayerTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot, + TileXRCcuLowerLayerTransportSnapshot *snapshot) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->ExportLowerLayerTransportSnapshot(templateSnapshot, snapshot); +} + +int TileXRCcuRuntimeSession::ConfigureLocalVerifiedEndpointRoute(const TileXRCcuLowerLayerTransportRoute &route) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute(route); +} + +int TileXRCcuRuntimeSession::RefreshLocalVerifiedEndpointRoute(TileXRCcuDirectRuntimeReport *report) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->RefreshLocalVerifiedEndpointRoute(report); +} + +int TileXRCcuRuntimeSession::CreateDriverAdapter( + TileXRCcuDriverAdapter *adapter, + TileXRCcuDriverAdapterReport *report) +{ + if (ccuDirectRuntime_ == nullptr || !ccuDirectRuntime_->IsAvailable()) { + if (report != nullptr) { + report->message = "direct CCU runtime is unavailable"; + } + return TILEXR_ERROR_NOT_FOUND; + } + return ccuDirectRuntime_->CreateDriverAdapter(adapter, report); +} + +int TileXRCcuRuntimeSession::AllGather(const void *sendBuf, size_t sendBytes, void *recvBuf) +{ + return DirectCcuAllGatherCallback(sendBuf, sendBytes, recvBuf, this); +} + +int TileXRCcuRuntimeSession::DirectCcuAllGatherCallback( + const void *sendBuf, + size_t sendBytes, + void *recvBuf, + void *userData) +{ + auto *session = static_cast(userData); + if (session == nullptr || sendBuf == nullptr || recvBuf == nullptr || sendBytes == 0) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (session->socketExchange_ == nullptr) { + return session->DirectCcuThreadAllGather(sendBuf, sendBytes, recvBuf); + } + return session->socketExchange_->AllGather( + static_cast(sendBuf), + sendBytes, + static_cast(recvBuf)); +} + +int TileXRCcuRuntimeSession::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 unavailableMessage = DirectCcuRuntimeUnavailableMessage(); + if (!unavailableMessage.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 << ", " + << unavailableMessage; + 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; + } + } +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_runtime_session.h b/src/comm/ccu/tilexr_ccu_runtime_session.h new file mode 100644 index 00000000..cde0802c --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_runtime_session.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2026 TileXR Project + */ +#ifndef TILEXR_CCU_RUNTIME_SESSION_H +#define TILEXR_CCU_RUNTIME_SESSION_H + +#include +#include +#include +#include +#include + +#include "ccu/tilexr_ccu_backend.h" +#include "ccu/tilexr_ccu_direct_runtime.h" + +namespace TileXR { + +class TileXRCcuRuntimeSession { +public: + int Init(const TileXRCcuBackendOptions &options); + void Shutdown(); + bool Available() const; + + int Rank() const; + int RankSize() const; + int DevId() const; + int AllGather(const void *sendBuf, size_t sendBytes, void *recvBuf); + + int RefreshDirectCcuBasicInfo(uint8_t dieId = 0); + bool HasDirectCcuBasicInfo() const; + int GetDirectCcuBasicInfoStatus() const; + const TileXRCcuBasicInfo *GetDirectCcuBasicInfo() const; + const TileXRCcuDriverAdapterReport &GetDirectCcuBasicInfoReport() const; + + int RegisterCcuResourceRmaBuffer(uint64_t resourceAddr); + int RegisterMemoryBuffer(uint64_t addr, uint64_t bytes, TileXRCcuRegisteredMemoryBufferInfo *info); + int ImportRemoteMemoryBuffer( + const TileXRCcuRemoteMemoryBufferImportRequest &request, + TileXRCcuImportedRemoteMemoryBufferInfo *info); + int ExportLocalCcuRmaBuffer(TileXRCcuLocalResourceWindowInfo *info); + int ExportRemoteCcuRmaBuffers(std::vector *buffers); + int ExportLowerLayerTransportSnapshot( + const TileXRCcuLowerLayerTransportSnapshot &templateSnapshot, + TileXRCcuLowerLayerTransportSnapshot *snapshot); + int ConfigureLocalVerifiedEndpointRoute(const TileXRCcuLowerLayerTransportRoute &route); + int RefreshLocalVerifiedEndpointRoute(TileXRCcuDirectRuntimeReport *report); + int CreateDriverAdapter(TileXRCcuDriverAdapter *adapter, TileXRCcuDriverAdapterReport *report); + + std::string DirectCcuRuntimeUnavailableMessage() const; + +private: + void ResetDirectCcuBasicInfo(); + static std::string DirectCcuRuntimeUnavailableMessageForDevice(int devId); + static int DirectCcuAllGatherCallback(const void *sendBuf, size_t sendBytes, void *recvBuf, void *userData); + int DirectCcuThreadAllGather(const void *sendBuf, size_t sendBytes, void *recvBuf); + + TileXRCcuBackendOptions options_ = {}; + int rank_ = 0; + int rankSize_ = 0; + int devId_ = 0; + std::string uid_ = {}; + TileXRSockExchange *socketExchange_ = nullptr; + bool initialized_ = false; + std::unique_ptr ccuDirectRuntime_; + bool directCcuBasicInfoValid_ = false; + int directCcuBasicInfoStatus_ = TILEXR_ERROR_NOT_FOUND; + TileXRCcuBasicInfo directCcuBasicInfo_ = {}; + TileXRCcuDriverAdapterReport directCcuBasicInfoReport_ = {}; + uint64_t directCcuThreadAllGatherRound_ = 0; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_RUNTIME_SESSION_H diff --git a/src/comm/ccu/tilexr_ccu_signal_wait_program.cpp b/src/comm/ccu/tilexr_ccu_signal_wait_program.cpp new file mode 100644 index 00000000..fb00e38a --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_signal_wait_program.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_signal_wait_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 HasSignalResources(const TileXRCcuSignalWaitProgramSpec& spec) +{ + return spec.channelId != 0 && + spec.remoteNotifyCke != 0 && + spec.remoteNotifyMask != 0 && + spec.sourceCke != 0 && + spec.sourceCkeMask != 0; +} + +bool HasWaitResources(const TileXRCcuSignalWaitProgramSpec& spec) +{ + return spec.localWaitCke != 0 && + spec.localWaitMask != 0; +} + +TileXRCcuBarrierSyncSpec ToBarrierSpec(const TileXRCcuSignalWaitProgramSpec& spec) +{ + TileXRCcuBarrierSyncSpec barrier; + barrier.channelId = spec.channelId; + barrier.remoteXn = spec.remoteXn; + barrier.localXn = spec.localXn; + barrier.remoteNotifyCke = spec.remoteNotifyCke; + barrier.remoteNotifyMask = spec.remoteNotifyMask; + barrier.localWaitCke = spec.localWaitCke; + barrier.localWaitMask = spec.localWaitMask; + barrier.sourceCke = spec.sourceCke; + barrier.sourceCkeMask = spec.sourceCkeMask; + barrier.clearLocalWait = spec.clearLocalWait; + return barrier; +} + +int AppendWaitInstruction( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report, + bool setWait) +{ + if (!HasWaitResources(spec)) { + return Fail(program, report, "missing wait CKE resource for direct CCU signal/wait program"); + } + TileXRCcuCkeSpec wait; + wait.waitCkeId = spec.localWaitCke; + wait.waitMask = spec.localWaitMask; + wait.clearWait = spec.clearLocalWait; + TileXRCcuInstr instr; + const int ret = setWait ? + TileXRCcuEncodeSetCke(wait, &instr) : + TileXRCcuEncodeClearCke(wait, &instr); + if (ret != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode wait instruction for direct CCU signal/wait program"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendCommonPrelude( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + if (spec.localXn == 0 || spec.sourceCke == 0 || spec.sourceCkeMask == 0) { + return Fail(program, report, "missing reserve resource for direct CCU signal/wait program"); + } + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(spec.localXn, 0, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal/wait reserve XN prelude"); + } + program->push_back(instr); + + if (spec.localGsa != 0) { + if (TileXRCcuEncodeLoadImdToGsa(spec.localGsa, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal/wait reserve GSA prelude"); + } + } else if (TileXRCcuEncodeLoadImdToXn(spec.localXn, 0, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal/wait reserve address prelude"); + } + program->push_back(instr); + + TileXRCcuCkeSpec init; + init.ckeId = spec.sourceCke; + init.mask = spec.sourceCkeMask; + init.clearWait = true; + if (TileXRCcuEncodeSetCke(init, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal/wait reserve CKE prelude"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendFinish( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + TileXRCcuInstr instr; + if (TileXRCcuEncodeLoadImdToXn(spec.localXn, 0, 0, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal/wait finish instruction"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int AppendSignalInstruction( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + if (!HasSignalResources(spec)) { + return Fail(program, report, "missing signal resource for direct CCU signal/wait program"); + } + TileXRCcuSyncCkeSpec post; + post.remoteCke = spec.remoteNotifyCke; + post.localCke = spec.sourceCke; + post.localCkeMask = spec.sourceCkeMask; + post.channelId = spec.channelId; + post.clearWait = spec.clearLocalWait; + TileXRCcuInstr instr; + if (TileXRCcuEncodeSyncCke(post, &instr) != TILEXR_SUCCESS) { + return Fail(program, report, "failed to encode signal instruction for direct CCU signal/wait program"); + } + program->push_back(instr); + return TILEXR_SUCCESS; +} + +int BuildWaitOnly( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + program->clear(); + const int ret = AppendWaitInstruction(spec, program, report, false); + if (ret != TILEXR_SUCCESS) { + return ret; + } + if (report != nullptr) { + report->postInstructionCount = 0; + report->waitInstructionCount = 1; + report->totalInstructionCount = static_cast(program->size()); + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +} // namespace + +int TileXRCcuBuildSignalWaitProgram( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report) +{ + ResetReport(report); + if (program == nullptr) { + return Fail(program, report, "missing output direct CCU signal/wait program"); + } + program->clear(); + + if (spec.role == TileXRCcuSignalWaitProgramRole::Wait) { + return BuildWaitOnly(spec, program, report); + } + + if (!HasSignalResources(spec)) { + return Fail(program, report, "missing signal resource for direct CCU signal/wait program"); + } + if (spec.role == TileXRCcuSignalWaitProgramRole::SignalAndWait && !HasWaitResources(spec)) { + return Fail(program, report, "missing wait CKE resource for direct CCU signal/wait program"); + } + + if (AppendCommonPrelude(spec, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AppendSignalInstruction(spec, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (spec.role == TileXRCcuSignalWaitProgramRole::SignalAndWait && + AppendWaitInstruction(spec, program, report, false) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AppendFinish(spec, program, report) != TILEXR_SUCCESS) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (report != nullptr) { + report->postInstructionCount = 1; + report->waitInstructionCount = spec.role == TileXRCcuSignalWaitProgramRole::SignalAndWait ? 1U : 0U; + report->totalInstructionCount = static_cast(program->size()); + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_signal_wait_program.h b/src/comm/ccu/tilexr_ccu_signal_wait_program.h new file mode 100644 index 00000000..63cbb7e8 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_signal_wait_program.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_SIGNAL_WAIT_PROGRAM_H +#define TILEXR_CCU_SIGNAL_WAIT_PROGRAM_H + +#include "ccu/tilexr_ccu_barrier_program.h" + +namespace TileXR { + +enum class TileXRCcuSignalWaitProgramRole { + Signal = 0, + Wait = 1, + SignalAndWait = 2, +}; + +struct TileXRCcuSignalWaitProgramSpec { + TileXRCcuSignalWaitProgramRole role = TileXRCcuSignalWaitProgramRole::Signal; + uint16_t channelId = 0; + uint16_t remoteXn = 0; + uint16_t localXn = 0; + uint16_t localGsa = 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; +}; + +int TileXRCcuBuildSignalWaitProgram( + const TileXRCcuSignalWaitProgramSpec& spec, + std::vector* program, + TileXRCcuBarrierProgramReport* report); + +} // namespace TileXR + +#endif // TILEXR_CCU_SIGNAL_WAIT_PROGRAM_H diff --git a/src/comm/ccu/tilexr_ccu_specs.cpp b/src/comm/ccu/tilexr_ccu_specs.cpp new file mode 100644 index 00000000..06cdcdec --- /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 00000000..75674e41 --- /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/ccu/tilexr_ccu_topology.cpp b/src/comm/ccu/tilexr_ccu_topology.cpp new file mode 100644 index 00000000..c03e6ee2 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_topology.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_topology.h" + +#include "tilexr_types.h" + +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +constexpr const char* TILEXR_CCU_DIRECT_FORCE_TP_TYPE_ENV = + "TILEXR_CCU_DIRECT_FORCE_TP_TYPE"; + +struct RootInfo { + std::string topoPath; + std::unordered_map deviceToLocalId; + std::unordered_map>> portToEidByLocalId; +}; + +struct TopoEdge { + uint32_t localA = 0; + uint32_t localB = 0; + std::vector localAPorts; + std::vector localBPorts; + bool supportsCtp = false; +}; + +std::string ReadTextFile(const std::string& path) +{ + std::ifstream input(path); + if (!input.is_open()) { + return {}; + } + return std::string(std::istreambuf_iterator(input), std::istreambuf_iterator()); +} + +bool ParseUint(const std::string& value, uint32_t* out) +{ + if (value.empty() || out == nullptr) { + return false; + } + char* end = nullptr; + const unsigned long parsed = std::strtoul(value.c_str(), &end, 10); + if (end == value.c_str() || *end != '\0' || parsed > UINT32_MAX) { + return false; + } + *out = static_cast(parsed); + return true; +} + +std::string JsonStringField(const std::string& object, const std::string& field) +{ + const std::regex pattern("\"" + field + "\"\\s*:\\s*\"([^\"]*)\""); + std::smatch match; + return std::regex_search(object, match, pattern) ? match[1].str() : std::string(); +} + +bool JsonUintField(const std::string& object, const std::string& field, uint32_t* out) +{ + const std::regex quoted("\"" + field + "\"\\s*:\\s*\"([0-9]+)\""); + const std::regex plain("\"" + field + "\"\\s*:\\s*([0-9]+)"); + std::smatch match; + if (std::regex_search(object, match, quoted) || std::regex_search(object, match, plain)) { + return ParseUint(match[1].str(), out); + } + return false; +} + +bool ParseEidHex(const std::string& text, std::array* eid) +{ + if (eid == nullptr || text.size() != eid->size() * 2U) { + return false; + } + for (size_t i = 0; i < eid->size(); ++i) { + const char hi = text[i * 2U]; + const char lo = text[i * 2U + 1U]; + if (!std::isxdigit(static_cast(hi)) || + !std::isxdigit(static_cast(lo))) { + return false; + } + (*eid)[i] = static_cast( + std::strtoul(text.substr(i * 2U, 2U).c_str(), nullptr, 16)); + } + return true; +} + +std::vector JsonStringArrayField(const std::string& object, const std::string& field) +{ + const std::regex arrayPattern("\"" + field + "\"\\s*:\\s*\\[([^\\]]*)\\]"); + std::smatch arrayMatch; + if (!std::regex_search(object, arrayMatch, arrayPattern)) { + return {}; + } + const std::string body = arrayMatch[1].str(); + std::vector values; + const std::regex valuePattern("\"([^\"]*)\""); + for (auto it = std::sregex_iterator(body.begin(), body.end(), valuePattern); + it != std::sregex_iterator(); ++it) { + values.push_back((*it)[1].str()); + } + return values; +} + +std::vector ExtractObjectsWithKey(const std::string& text, const std::string& key) +{ + std::vector objects; + const std::string needle = "\"" + key + "\""; + size_t pos = 0; + while ((pos = text.find(needle, pos)) != std::string::npos) { + const size_t begin = text.rfind('{', pos); + if (begin == std::string::npos) { + ++pos; + continue; + } + int depth = 0; + bool inString = false; + bool escaped = false; + for (size_t i = begin; i < text.size(); ++i) { + const char ch = text[i]; + if (inString) { + escaped = !escaped && ch == '\\'; + if (ch == '"' && !escaped) { + inString = false; + } else if (ch != '\\') { + escaped = false; + } + continue; + } + if (ch == '"') { + inString = true; + } else if (ch == '{') { + ++depth; + } else if (ch == '}') { + --depth; + if (depth == 0) { + objects.emplace_back(text.substr(begin, i - begin + 1U)); + pos = i + 1U; + break; + } + } + } + if (depth != 0) { + break; + } + } + return objects; +} + +bool ParseRootInfo(const std::string& path, RootInfo* root) +{ + if (root == nullptr) { + return false; + } + const std::string content = ReadTextFile(path); + root->topoPath = JsonStringField(content, "topo_file_path"); + if (content.empty() || root->topoPath.empty()) { + return false; + } + for (const auto& rankObject : ExtractObjectsWithKey(content, "device_id")) { + uint32_t deviceId = 0; + uint32_t localId = 0; + if (!JsonUintField(rankObject, "device_id", &deviceId) || + !JsonUintField(rankObject, "local_id", &localId)) { + continue; + } + root->deviceToLocalId[deviceId] = localId; + for (const auto& addressObject : ExtractObjectsWithKey(rankObject, "addr")) { + std::array eid {}; + if (!ParseEidHex(JsonStringField(addressObject, "addr"), &eid)) { + continue; + } + for (const auto& port : JsonStringArrayField(addressObject, "ports")) { + root->portToEidByLocalId[localId][port] = eid; + } + } + } + return !root->deviceToLocalId.empty(); +} + +std::vector ParseTopoInfo(const std::string& path) +{ + const std::string content = ReadTextFile(path); + std::vector edges; + for (const auto& edgeObject : ExtractObjectsWithKey(content, "local_a")) { + TopoEdge edge; + if (!JsonUintField(edgeObject, "local_a", &edge.localA) || + !JsonUintField(edgeObject, "local_b", &edge.localB)) { + continue; + } + edge.localAPorts = JsonStringArrayField(edgeObject, "local_a_ports"); + edge.localBPorts = JsonStringArrayField(edgeObject, "local_b_ports"); + const auto protocols = JsonStringArrayField(edgeObject, "protocols"); + edge.supportsCtp = std::find(protocols.begin(), protocols.end(), "UB_CTP") != protocols.end(); + if (!edge.localAPorts.empty() && !edge.localBPorts.empty()) { + edges.push_back(edge); + } + } + return edges; +} + +bool ResolveLocalPort( + const std::vector& edges, + uint32_t localId, + uint32_t peerLocalId, + std::string* localPort, + bool* supportsCtp) +{ + if (localPort == nullptr || supportsCtp == nullptr) { + return false; + } + for (const auto& edge : edges) { + if (edge.localA == localId && edge.localB == peerLocalId) { + *localPort = edge.localAPorts.front(); + *supportsCtp = edge.supportsCtp; + return true; + } + if (edge.localB == localId && edge.localA == peerLocalId) { + *localPort = edge.localBPorts.front(); + *supportsCtp = edge.supportsCtp; + return true; + } + } + return false; +} + +int ForcedTpType() +{ + const char* value = std::getenv(TILEXR_CCU_DIRECT_FORCE_TP_TYPE_ENV); + if (value == nullptr) { + return -1; + } + const std::string text(value); + if (text == "rtp" || text == "RTP" || text == "0") { + return static_cast(TILEXR_CCU_HCCP_TP_TYPE_RTP); + } + if (text == "ctp" || text == "CTP" || text == "1") { + return static_cast(TILEXR_CCU_HCCP_TP_TYPE_CTP); + } + return -1; +} + +} // namespace + +int TileXRCcuResolvePeerEidRoutes( + const std::string& rootInfoPath, + uint32_t localDevicePhyId, + const std::vector& peerDevicePhyIds, + std::vector* routes, + std::string* message) +{ + if (routes == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + routes->clear(); + RootInfo root; + if (!ParseRootInfo(rootInfoPath, &root)) { + if (message != nullptr) { + *message = "failed to parse HCCL root info"; + } + return TILEXR_ERROR_NOT_FOUND; + } + const auto localIdIt = root.deviceToLocalId.find(localDevicePhyId); + if (localIdIt == root.deviceToLocalId.end()) { + if (message != nullptr) { + *message = "local physical device is absent from HCCL root info"; + } + return TILEXR_ERROR_NOT_FOUND; + } + const auto edges = ParseTopoInfo(root.topoPath); + const auto eidMapIt = root.portToEidByLocalId.find(localIdIt->second); + if (edges.empty() || eidMapIt == root.portToEidByLocalId.end()) { + if (message != nullptr) { + *message = "HCCL topology has no local EID routes"; + } + return TILEXR_ERROR_NOT_FOUND; + } + for (const uint32_t peerDevicePhyId : peerDevicePhyIds) { + const auto peerIdIt = root.deviceToLocalId.find(peerDevicePhyId); + std::string localPort; + bool supportsCtp = false; + if (peerIdIt == root.deviceToLocalId.end() || + !ResolveLocalPort(edges, localIdIt->second, peerIdIt->second, &localPort, &supportsCtp)) { + if (message != nullptr) { + *message = "HCCL topology has no device-pair edge"; + } + routes->clear(); + return TILEXR_ERROR_NOT_FOUND; + } + const auto eidIt = eidMapIt->second.find(localPort); + if (eidIt == eidMapIt->second.end()) { + if (message != nullptr) { + *message = "HCCL root info has no EID for the selected local port"; + } + routes->clear(); + return TILEXR_ERROR_NOT_FOUND; + } + TileXRCcuPeerEidRoute route; + route.peerDevicePhyId = peerDevicePhyId; + route.localEid = eidIt->second; + route.localPort = localPort; + const int forcedTpType = ForcedTpType(); + route.tpType = forcedTpType >= 0 ? + static_cast(forcedTpType) : + (supportsCtp ? TILEXR_CCU_HCCP_TP_TYPE_CTP : TILEXR_CCU_HCCP_TP_TYPE_RTP); + routes->push_back(route); + } + if (message != nullptr) { + *message = "ok"; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_topology.h b/src/comm/ccu/tilexr_ccu_topology.h new file mode 100644 index 00000000..de8ac29f --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_topology.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_TOPOLOGY_H +#define TILEXR_CCU_TOPOLOGY_H + +#include "ccu/tilexr_ccu_hccp_types.h" +#include "tilexr_types.h" + +#include +#include +#include +#include + +namespace TileXR { + +struct TileXRCcuPeerEidRoute { + uint32_t peerDevicePhyId = 0; + std::array localEid {}; + std::string localPort; + uint32_t tpType = TILEXR_CCU_HCCP_TP_TYPE_RTP; +}; + +int TileXRCcuResolvePeerEidRoutes( + const std::string& rootInfoPath, + uint32_t localDevicePhyId, + const std::vector& peerDevicePhyIds, + std::vector* routes, + std::string* message); + +} // namespace TileXR + +#endif // TILEXR_CCU_TOPOLOGY_H diff --git a/src/comm/tilexr_comm.cpp b/src/comm/tilexr_comm.cpp index 69736d0d..95e0227f 100644 --- a/src/comm/tilexr_comm.cpp +++ b/src/comm/tilexr_comm.cpp @@ -9,6 +9,7 @@ */ #include "tilexr_comm.h" #include "tilexr_internal.h" +#include "ccu/tilexr_ccu_backend.h" #include "sdma/tilexr_sdma_transport.h" #include "udma/tilexr_udma_context.h" @@ -22,13 +23,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 { @@ -53,6 +55,17 @@ static std::mutex g_mtx; static std::mutex g_sdmaMtx; static bool g_sdmaUnavailable = false; +constexpr const char* TILEXR_ENABLE_CCU_BACKEND_ENV = "TILEXR_ENABLE_CCU_BACKEND"; + +bool ShouldEnableCcuBackend() +{ + const char *value = std::getenv(TILEXR_ENABLE_CCU_BACKEND_ENV); + if (value == nullptr || value[0] == '\0') { + return false; + } + const string text(value); + return text == "1" || text == "true" || text == "TRUE" || text == "yes" || text == "on"; +} // 如果是互联的链路,返回false; 对910B2C那些不互联的链路,返回true bool SkipUnusedChannel910B2C(int curRank, int peerRank, ChipName chipName) @@ -181,6 +194,59 @@ int TileXRComm::ApplyUDMACommArgsState(const TileXRUDMACommArgsState &state) return TILEXR_SUCCESS; } +int TileXRComm::InitCcuBackend() +{ + if (ccuBackend_ == nullptr) { + ccuBackend_.reset(new (nothrow) TileXRCcuBackend()); + if (ccuBackend_ == nullptr) { + return TILEXR_ERROR_INTERNAL; + } + } + + TileXRCcuBackendOptions options {}; + options.rank = rank_; + options.rankSize = rankSize_; + options.devId = devId_; + options.uid = uid_; + options.exchange = socketExchange_; + return ccuBackend_->Init(options); +} + +int TileXRComm::InitCcuBackendIfEnabled() +{ + if (!ShouldEnableCcuBackend()) { + return TILEXR_SUCCESS; + } + + const int ccuRet = InitCcuBackend(); + if (ccuRet != TILEXR_SUCCESS || ccuBackend_ == nullptr || !ccuBackend_->Available()) { + TILEXR_LOG(WARN) << "TileXR CCU backend init failed, direct CCU disabled, ret: " << ccuRet; + if (ccuBackend_ != nullptr) { + ccuBackend_->Shutdown(); + ccuBackend_.reset(); + } + return TILEXR_SUCCESS; + } + + TILEXR_LOG(INFO) << "TileXR CCU backend initialized"; + return TILEXR_SUCCESS; +} + +TileXRCcuBackend *TileXRComm::GetCcuBackendForCollectives() +{ + return ccuBackend_.get(); +} + +const TileXRCcuBackend *TileXRComm::GetCcuBackendForCollectives() const +{ + return ccuBackend_.get(); +} + +int TileXRComm::EnableCcuBackendForTest() +{ + return InitCcuBackend(); +} + int TileXRComm::ApplyUDMACommArgsStateCallback(const TileXRUDMACommArgsState &state, void *userData) { if (userData == nullptr) { @@ -360,6 +426,11 @@ const TileXRUDMARegistry* TileXRComm::GetUDMARegistryHost() const return udmaContext_ == nullptr ? nullptr : udmaContext_->GetRegistryHost(); } +bool TileXRComm::IsUdmaAvailableForCollectives() const +{ + return udmaContext_ != nullptr && udmaContext_->IsAvailable(); +} + int TileXRComm::InitCommon() { // enable peer device @@ -465,6 +536,10 @@ int TileXRComm::Init() if (ret != TILEXR_SUCCESS) { return ret; } + ret = InitCcuBackendIfEnabled(); + if (ret != TILEXR_SUCCESS) { + return ret; + } // set comm args in device. ret = SyncCommArgs(); @@ -537,6 +612,10 @@ int TileXRComm::InitThread(const std::string &uid) if (ret != TILEXR_SUCCESS) { return ret; } + ret = InitCcuBackendIfEnabled(); + if (ret != TILEXR_SUCCESS) { + return ret; + } ret = SyncCommArgs(); if (ret != TILEXR_SUCCESS) { TILEXR_LOG(ERROR) << "SyncCommArgs failed! ret: " << ret; @@ -815,6 +894,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; } @@ -887,6 +969,10 @@ TileXRComm::~TileXRComm() FreePeerMem(commArgs_.dumpAddr); FreePeerMem(peerMem_[rank_]); FreePeerMem(commArgsPtr_); + if (ccuBackend_ != nullptr) { + ccuBackend_->Shutdown(); + ccuBackend_.reset(); + } ResetSDMAState(); } diff --git a/src/comm/tilexr_comm.h b/src/comm/tilexr_comm.h index 8b8af295..48dda0c3 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 @@ -27,6 +28,7 @@ class TileXRSockExchange; class TileXRUDMAContext; struct TileXRUDMACommArgsState; class TileXRSDMATransport; +class TileXRCcuBackend; class TileXRComm { public: TileXRComm(int rank, int rankSize); @@ -48,6 +50,11 @@ class TileXRComm { int UnregisterUDMAMemory(TileXRUDMAMemHandle handle); GM_ADDR GetUDMARegistryPtr() const; const TileXRUDMARegistry* GetUDMARegistryHost() const; + bool IsUdmaAvailableForCollectives() const; + int InitCcuBackend(); + TileXRCcuBackend *GetCcuBackendForCollectives(); + const TileXRCcuBackend *GetCcuBackendForCollectives() const; + int EnableCcuBackendForTest(); bool IsSDMAAvailable() const; GM_ADDR GetSDMAWorkspacePtr() const; SDMAInitStatus GetSDMAInitStatus() const; @@ -76,6 +83,7 @@ class TileXRComm { int InitUDMA(); int ApplyUDMACommArgsState(const TileXRUDMACommArgsState &state); static int ApplyUDMACommArgsStateCallback(const TileXRUDMACommArgsState &state, void *userData); + int InitCcuBackendIfEnabled(); int InitSDMA(); int UpdateCommArgsDev(); void ResetSDMAState(); @@ -104,6 +112,7 @@ class TileXRComm { TileXRSockExchange *socketExchange_ = nullptr; bool isEnableMsprofOp_ = false; std::unique_ptr udmaContext_; + std::unique_ptr ccuBackend_; 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 e39bd332..3f53f246 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 7b4fd3a7..13cf8251 100644 --- a/src/include/tilexr_api.h +++ b/src/include/tilexr_api.h @@ -10,10 +10,18 @@ #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 @@ -33,9 +41,11 @@ int TileXRCommInitRankWithCustDomainSize(int commDomain, int bufferSize, int ran int TileXRCommInitRankWithDomain(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,9 +53,11 @@ 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); diff --git a/src/include/tilexr_collectives.h b/src/include/tilexr_collectives.h index 50fa7828..b2bac393 100644 --- a/src/include/tilexr_collectives.h +++ b/src/include/tilexr_collectives.h @@ -20,21 +20,51 @@ // The collectives public API is currently C++ header-compatible because it reuses TileXR namespace datatypes. extern "C" { +enum TileXRCollectiveBackend { + TILEXR_COLLECTIVE_BACKEND_AUTO = 0, + TILEXR_COLLECTIVE_BACKEND_AIV = 1, + TILEXR_COLLECTIVE_BACKEND_UDMA = 2, + TILEXR_COLLECTIVE_BACKEND_CCU = 3, +}; + +struct TileXRCollectiveOptions { + TileXRCollectiveBackend backend; +}; + +int TileXRAllGatherEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); int TileXRAllGather(void *sendBuf, void *recvBuf, int64_t sendCount, TileXR::TileXRDataType dataType, TileXRCommPtr comm, aclrtStream stream); +int TileXRAllToAllEx(void *sendBuf, void *recvBuf, int64_t sendCount, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); int TileXRAllToAll(void *sendBuf, void *recvBuf, int64_t sendCount, TileXR::TileXRDataType dataType, TileXRCommPtr comm, aclrtStream stream); +int TileXRAllReduceEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options); int TileXRAllReduce(void *sendBuf, void *recvBuf, int64_t count, TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, TileXRCommPtr comm, aclrtStream stream); +int TileXRReduceScatterEx(void *sendBuf, void *recvBuf, int64_t recvCount, + TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, + TileXRCommPtr comm, aclrtStream stream, + const TileXRCollectiveOptions *options); int TileXRReduceScatter(void *sendBuf, void *recvBuf, int64_t recvCount, TileXR::TileXRDataType dataType, TileXR::TileXRReduceOp op, TileXRCommPtr comm, aclrtStream stream); +int TileXRBroadcastEx(void *buf, int64_t count, + TileXR::TileXRDataType dataType, int root, + TileXRCommPtr comm, aclrtStream stream, const TileXRCollectiveOptions *options); int TileXRBroadcast(void *buf, int64_t count, TileXR::TileXRDataType dataType, int root, TileXRCommPtr comm, aclrtStream stream); +int TileXRProfileProbeEx(void *sendBuf, void *recvBuf, int64_t count, + TileXR::TileXRDataType dataType, TileXRCommPtr comm, + aclrtStream stream, const TileXRCollectiveOptions *options); int TileXRProfileProbe(void *sendBuf, void *recvBuf, int64_t count, TileXR::TileXRDataType dataType, TileXRCommPtr comm, aclrtStream stream); diff --git a/tests/ccu/CMakeLists.txt b/tests/ccu/CMakeLists.txt new file mode 100644 index 00000000..212090fc --- /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 00000000..8659f4f7 --- /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 00000000..a8f103e5 --- /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_tilexr_basic_info_probe.cpp b/tests/ccu/ccu_tilexr_basic_info_probe.cpp new file mode 100644 index 00000000..73e673fb --- /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 00000000..a486fb03 --- /dev/null +++ b/tests/ccu/ccu_tilexr_direct_smoke_probe.cpp @@ -0,0 +1,3076 @@ +/* + * 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 "ccu/tilexr_ccu_backend.h" +#include "ccu/tilexr_ccu_collective_planner.h" +#include "ccu/tilexr_ccu_driver_adapter.h" +#include "ccu/tilexr_ccu_executor.h" +#include "ccu/tilexr_ccu_runtime_session.h" +#include "tools/socket/tilexr_sock_exchange.h" +#include "runtime/dev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +struct DirectCcuSmokeContext { + std::unique_ptr exchange; + TileXR::TileXRCcuRuntimeSession session; + TileXR::TileXRCcuCollectivePlanner planner; + TileXR::TileXRCcuExecutor executor; + TileXR::TileXRCcuBackend backend; +}; + +using TileXRDirectCcuPrepareOptions = TileXR::TileXRCcuDirectInstallOptions; +using TileXRDirectCcuPrepareReport = TileXR::TileXRCcuDirectInstallReport; +using TileXRDirectCcuSubmitReport = TileXR::TileXRCcuDirectSubmitReport; +using TileXRDirectCcuPreparedTasksPtr = TileXR::TileXRCcuDirectInstallAttempt*; +using TileXRDirectCcuTaskInfo = TileXR::TileXRCcuTask; +using TileXRDirectCcuInstructionReadbackReport = TileXR::TileXRCcuDriverAdapterReport; + +constexpr uint32_t TILEXR_DIRECT_CCU_SQE_ARGS_LEN = TileXR::TILEXR_CCU_SQE_ARGS_LEN; + +struct TileXRDirectCcuInstructionWords { + uint32_t words[4] = {}; +}; + +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* kP2pCcuCopyActiveRankEnv = "TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK"; +constexpr const char* kP2pCcuCopyDirectionEnv = "TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION"; +constexpr const char* kP2pCcuCopyResourceWindowEnv = "TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_RESOURCE_WINDOW"; +constexpr const char* kAllToAllEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL"; +constexpr const char* kAllToAllLongMissionEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION"; +constexpr const char* kAllToAllMeshEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH"; +constexpr const char* kAllToAllSingleRouteBidirectionalEnv = + "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_SINGLE_ROUTE_BIDIRECTIONAL"; +constexpr const char* kAllToAllBytesEnv = "TILEXR_CCU_ALLTOALL_BYTES"; +constexpr const char* kAllToAllMemSlicePerLoopEnv = "TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP"; +constexpr const char* kAllToAllLoopCountEnv = "TILEXR_CCU_ALLTOALL_LOOP_COUNT"; +constexpr const char* kSyncXnPingEnv = "TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING"; +constexpr const char* kSyncXnPingPeerXorEnv = "TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR"; +constexpr const char* kSignalWaitEnv = "TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT"; +constexpr const char* kSignalWaitSignalRankEnv = "TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK"; +constexpr const char* kSignalWaitBarrierEnv = "TILEXR_CCU_DIRECT_SMOKE_BARRIER"; +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; +}; + +struct AllToAllState { + DeviceBuffer source; + DeviceBuffer destination; + std::vector expected; + std::vector observed; + size_t bytes = 0; + size_t chunkBytes = 0; + int rankSize = 0; + int initRet = ACL_SUCCESS; + int readRet = ACL_SUCCESS; + uint32_t mismatchCount = 0; + size_t firstMismatchOffset = 0; + size_t lastMismatchOffset = 0; + uint32_t firstMismatchObserved = 0; + uint32_t firstMismatchExpected = 0; + uint32_t mismatchedBlockCount = 0; + uint32_t firstMismatchedBlock = 0; + uint32_t lastMismatchedBlock = 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 SignalWaitSmokeEnabled() +{ + return EnvFlag(kSignalWaitEnv); +} + +bool AllToAllSmokeEnabled() +{ + return EnvFlag(kAllToAllEnv); +} + +bool AllToAllLongMissionEnabled() +{ + return EnvFlag(kAllToAllLongMissionEnv); +} + +bool AllToAllMeshSmokeEnabled() +{ + return EnvFlag(kAllToAllMeshEnv); +} + +bool AllToAllSingleRouteBidirectionalEnabled() +{ + return EnvFlag(kAllToAllSingleRouteBidirectionalEnv); +} + +bool SyncXnPingSmokeEnabled() +{ + return EnvFlag(kSyncXnPingEnv); +} + +bool BarrierSmokeEnabled() +{ + return EnvFlag(kSignalWaitBarrierEnv); +} + +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); +} + +bool IsP2pCcuCopyActiveRank(int rank) +{ + return rank == EnvInt(kP2pCcuCopyActiveRankEnv, 0); +} + +TileXR::TileXRCcuMemoryCopyDirection P2pCcuCopyDirectionFromEnv() +{ + const char* value = std::getenv(kP2pCcuCopyDirectionEnv); + const std::string direction = value == nullptr ? "" : std::string(value); + if (direction == "local_to_remote" || direction == "LocalToRemote" || direction == "1") { + return TileXR::TileXRCcuMemoryCopyDirection::LocalToRemote; + } + return TileXR::TileXRCcuMemoryCopyDirection::RemoteToLocal; +} + +bool ShouldCheckInactiveP2pCcuCopyRank() +{ + return P2pCcuCopyDirectionFromEnv() == TileXR::TileXRCcuMemoryCopyDirection::LocalToRemote; +} + +bool ShouldCheckActiveP2pCcuCopyRank() +{ + return P2pCcuCopyDirectionFromEnv() == TileXR::TileXRCcuMemoryCopyDirection::RemoteToLocal; +} + +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"; +} + +TileXR::TileXRCcuBarrierMode BarrierModeFromEnv() +{ + if (SyncCkeBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::SyncCke; + } + if (SyncCkeSetWaitBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::SyncCkeSetWait; + } + if (SyncCkePostOnlyBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::SyncCkePostOnly; + } + if (LocalCkeBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::LocalCke; + } + if (LocalCkePostOnlyBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::LocalCkePostOnly; + } + if (SyncXnPostOnlyBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::SyncXnPostOnly; + } + if (SyncXnLoadPostOnlyBarrierMode()) { + return TileXR::TileXRCcuBarrierMode::SyncXnLoadPostOnly; + } + return TileXR::TileXRCcuBarrierMode::SyncXn; +} + +TileXR::TileXRCcuRepositoryInstallWindow RepositoryInstallWindowFromEnv() +{ + const char* value = std::getenv(kRepositoryInstallWindowEnv); + if (value == nullptr || value[0] == '\0') { + return TileXR::TileXRCcuRepositoryInstallWindow::Mission; + } + const std::string text(value); + if (text == "full_repository" || text == "full" || text == "1") { + return TileXR::TileXRCcuRepositoryInstallWindow::FullRepository; + } + return TileXR::TileXRCcuRepositoryInstallWindow::Mission; +} + +TileXR::TileXRCcuRepositoryInstallDataLenMode RepositoryInstallDataLenModeFromEnv() +{ + const char* value = std::getenv(kRepositoryInstallDataLenModeEnv); + if (value == nullptr || value[0] == '\0') { + return TileXR::TileXRCcuRepositoryInstallDataLenMode::InstructionBytes; + } + const std::string text(value); + if (text == "descriptor_bytes" || text == "descriptor" || text == "1") { + return TileXR::TileXRCcuRepositoryInstallDataLenMode::DescriptorBytes; + } + return TileXR::TileXRCcuRepositoryInstallDataLenMode::InstructionBytes; +} + +TileXR::TileXRCcuRepositoryMemoryAllocMode RepositoryMemoryAllocModeFromEnv() +{ + const char* value = std::getenv(kRepositoryMemoryAllocModeEnv); + if (value == nullptr || value[0] == '\0') { + return TileXR::TileXRCcuRepositoryMemoryAllocMode::Acl; + } + const std::string text(value); + if (text == "acl_module3" || text == "acl_hccl_module" || text == "module3" || text == "1") { + return TileXR::TileXRCcuRepositoryMemoryAllocMode::AclModule3; + } + if (text == "rt_hbm" || text == "rt" || text == "runtime_hbm" || text == "2") { + return TileXR::TileXRCcuRepositoryMemoryAllocMode::RtHbm; + } + return TileXR::TileXRCcuRepositoryMemoryAllocMode::Acl; +} + +TileXR::TileXRCcuInstallOrder InstallOrderFromEnv() +{ + const char* value = std::getenv(kInstallOrderEnv); + if (value == nullptr || value[0] == '\0') { + return TileXR::TileXRCcuInstallOrder::InstallLowerLayerFirst; + } + const std::string text(value); + if (text == "lower_layer_first" || text == "install_lower_layer_first" || text == "1") { + return TileXR::TileXRCcuInstallOrder::InstallLowerLayerFirst; + } + if (text == "repository_first" || text == "repo_first" || text == "0") { + return TileXR::TileXRCcuInstallOrder::RepositoryFirst; + } + return TileXR::TileXRCcuInstallOrder::RepositoryFirst; +} + +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; +} + +size_t AllToAllBytesFromEnv() +{ + return static_cast(EnvInt(kAllToAllBytesEnv, 2 * 1024 * 1024)); +} + +int AllToAllMemSlicePerLoopFromEnv() +{ + return EnvInt(kAllToAllMemSlicePerLoopEnv, 8); +} + +int AllToAllLoopCountFromEnv() +{ + const char* value = std::getenv(kAllToAllLoopCountEnv); + if (value == nullptr || value[0] == '\0') { + return 1; + } + char* end = nullptr; + const long parsed = std::strtol(value, &end, 10); + if (end == value || *end != '\0' || parsed < 1 || parsed > 1024) { + return 0; + } + return static_cast(parsed); +} + +uint64_t BuildAllToAllLoopMarker(int rank, int loopIndex) +{ + return 0x4343554c00000000ULL | + (static_cast(rank & 0xff) << 16U) | + static_cast(loopIndex & 0xffff); +} + +uint8_t BuildAllToAllMeshByte( + uint32_t sourceRank, + uint32_t targetRank, + uint32_t loopIndex, + size_t chunkOffset) +{ + return static_cast( + ((sourceRank + 1U) * 67U + (targetRank + 1U) * 29U + + (loopIndex + 1U) * 17U + chunkOffset * 13U) & 0xffU); +} + +int InitAllToAllMeshState(int rank, int rankSize, AllToAllState* state) +{ + if (state == nullptr || rankSize < 2 || rankSize > 64 || rank < 0 || rank >= rankSize) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + state->chunkBytes = AllToAllBytesFromEnv(); + state->rankSize = rankSize; + state->bytes = static_cast(rankSize) * state->chunkBytes; + const bool supportedChunkBytes = + state->chunkBytes == 128U * 1024U || state->chunkBytes == 2U * 1024U * 1024U; + if (!supportedChunkBytes || + state->bytes / state->chunkBytes != static_cast(rankSize) || + AllToAllMemSlicePerLoopFromEnv() != 8) { + state->initRet = TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + return state->initRet; + } + state->expected.assign(state->bytes, 0); + state->observed.assign(state->bytes, 0); + std::vector initial(state->bytes, 0xa5U); + int ret = state->source.Allocate(state->bytes); + if (ret == ACL_SUCCESS) { + ret = state->destination.Allocate(state->bytes); + } + if (ret == ACL_SUCCESS) { + ret = aclrtMemcpy( + state->source.ptr, state->bytes, initial.data(), initial.size(), ACL_MEMCPY_HOST_TO_DEVICE); + } + if (ret == ACL_SUCCESS) { + ret = aclrtMemcpy( + state->destination.ptr, state->bytes, initial.data(), initial.size(), ACL_MEMCPY_HOST_TO_DEVICE); + } + state->initRet = ret; + return ret; +} + +int ResetAllToAllMeshStateForLoop(int rank, int loopIndex, AllToAllState* state) +{ + if (state == nullptr || state->source.ptr == nullptr || state->destination.ptr == nullptr || + state->rankSize < 2 || state->rankSize > 64 || + state->bytes != static_cast(state->rankSize) * state->chunkBytes || + loopIndex < 0) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + std::vector source(state->bytes); + state->expected.assign(state->bytes, 0); + for (int targetRank = 0; targetRank < state->rankSize; ++targetRank) { + for (size_t chunkOffset = 0; chunkOffset < state->chunkBytes; ++chunkOffset) { + source[static_cast(targetRank) * state->chunkBytes + chunkOffset] = + BuildAllToAllMeshByte(rank, targetRank, loopIndex, chunkOffset); + } + } + for (int sourceRank = 0; sourceRank < state->rankSize; ++sourceRank) { + for (size_t chunkOffset = 0; chunkOffset < state->chunkBytes; ++chunkOffset) { + state->expected[static_cast(sourceRank) * state->chunkBytes + chunkOffset] = + BuildAllToAllMeshByte(sourceRank, rank, loopIndex, chunkOffset); + } + } + state->observed.assign(state->bytes, 0); + state->readRet = ACL_SUCCESS; + state->mismatchCount = 0; + state->firstMismatchOffset = 0; + state->lastMismatchOffset = 0; + state->firstMismatchObserved = 0; + state->firstMismatchExpected = 0; + state->mismatchedBlockCount = 0; + state->firstMismatchedBlock = 0; + state->lastMismatchedBlock = 0; + state->passed = false; + std::vector destination( + state->bytes, static_cast(0xa5U ^ static_cast(loopIndex))); + int ret = aclrtMemcpy( + state->source.ptr, state->bytes, source.data(), source.size(), ACL_MEMCPY_HOST_TO_DEVICE); + if (ret != ACL_SUCCESS) { + return ret; + } + return aclrtMemcpy( + state->destination.ptr, + state->bytes, + destination.data(), + destination.size(), + ACL_MEMCPY_HOST_TO_DEVICE); +} + +int InitAllToAllState(int rank, int peer, AllToAllState* state) +{ + if (state == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + state->bytes = AllToAllBytesFromEnv(); + const bool supportedBytes = state->bytes == 2U * 1024U * 1024U || + state->bytes == 8U * 1024U * 1024U || + state->bytes == 16U * 1024U * 1024U; + if (!supportedBytes || AllToAllMemSlicePerLoopFromEnv() != 8) { + 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 CheckAllToAllState(AllToAllState* 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; + state->firstMismatchOffset = 0; + state->lastMismatchOffset = 0; + state->firstMismatchObserved = 0; + state->firstMismatchExpected = 0; + state->mismatchedBlockCount = 0; + state->firstMismatchedBlock = 0; + state->lastMismatchedBlock = 0; + const size_t blockBytes = 8U * 4096U; + uint32_t currentBlock = UINT32_MAX; + for (size_t i = 0; i < state->bytes; ++i) { + if (state->observed[i] != state->expected[i]) { + if (state->mismatchCount == 0) { + state->firstMismatchOffset = i; + state->firstMismatchObserved = state->observed[i]; + state->firstMismatchExpected = state->expected[i]; + } + state->lastMismatchOffset = i; + const uint32_t block = static_cast(i / blockBytes); + if (block != currentBlock) { + if (state->mismatchedBlockCount == 0) { + state->firstMismatchedBlock = block; + } + state->lastMismatchedBlock = block; + currentBlock = block; + ++state->mismatchedBlockCount; + } + ++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; +} + +void PrintP2pCcuCopySkipped(int rank, int peer, const P2pCcuCopyState& state) +{ + std::cout << "tilexr_ccu_direct_smoke p2pCcuCopy skipped" + << " rank=" << rank + << " peer=" << peer + << " bytes=" << state.bytes + << " activeRank=" << EnvInt(kP2pCcuCopyActiveRankEnv, 0) + << " reason=\"inactive p2p CCU-copy rank\"" + << 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 (ShouldCheckActiveP2pCcuCopyRank() && syncRet == ACL_SUCCESS && submitRet == TileXR::TILEXR_SUCCESS) { + if (EnvFlag(kP2pCcuCopyResourceWindowEnv)) { + state->passed = true; + } else { + (void)CheckP2pCcuCopyState(state); + } + } else if (!ShouldCheckActiveP2pCcuCopyRank() && syncRet == ACL_SUCCESS && submitRet == TileXR::TILEXR_SUCCESS) { + state->passed = true; + } + 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, int device, DirectCcuSmokeContext* context) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + context->exchange.reset(new (std::nothrow) TileXR::TileXRSockExchange(rank, rankSize, commDomain)); + if (context->exchange == nullptr) { + return TileXR::TILEXR_ERROR_INTERNAL; + } + + TileXR::TileXRCcuBackendOptions options {}; + options.rank = rank; + options.rankSize = rankSize; + options.devId = device; + options.uid = "tilexr-direct-smoke-probe"; + options.exchange = context->exchange.get(); + const int ret = context->session.Init(options); + if (ret == TileXR::TILEXR_SUCCESS) { + std::cout << "tilexr_ccu_direct_smoke internalDirectCcuInit" + << " rank=" << rank + << " rankSize=" << rankSize + << " device=" << device + << " directOnly=" << (EnvFlag(kDirectCcuOnlyInitEnv) ? 1 : 0) + << std::endl; + } + if (ret == TileXR::TILEXR_SUCCESS && (SignalWaitSmokeEnabled() || BarrierSmokeEnabled())) { + const int backendRet = context->backend.Init(options); + std::cout << "tilexr_ccu_signal_wait backendInit" + << " rank=" << rank + << " rankSize=" << rankSize + << " device=" << device + << " ret=" << backendRet + << std::endl; + return backendRet; + } + return ret; +} + +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.repositoryInstallOptions.window = RepositoryInstallWindowFromEnv(); + options.repositoryInstallOptions.dataLenMode = RepositoryInstallDataLenModeFromEnv(); + options.repositoryMemoryAllocMode = RepositoryMemoryAllocModeFromEnv(); + options.installOrder = InstallOrderFromEnv(); + options.barrierMode = BarrierModeFromEnv(); + options.taskTimeout = static_cast(EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 0)); + options.deviceId = static_cast(device); + options.rank = static_cast(rank); + options.provider = rankSize > 0 ? "tilexr-direct-smoke-probe" : ""; + return options; +} + +const char* SignalWaitRoleName(TileXR::TileXRCcuSignalWaitRole role) +{ + switch (role) { + case TileXR::TileXRCcuSignalWaitRole::Signal: + return "signal"; + case TileXR::TileXRCcuSignalWaitRole::Wait: + return "wait"; + case TileXR::TileXRCcuSignalWaitRole::SignalAndWait: + return "signal_and_wait"; + default: + return "unknown"; + } +} + +TileXR::TileXRCcuSignalWaitRole SignalWaitRoleForRank(int rank) +{ + if (BarrierSmokeEnabled()) { + return TileXR::TileXRCcuSignalWaitRole::SignalAndWait; + } + const int signalRank = EnvInt(kSignalWaitSignalRankEnv, 0); + return rank == signalRank ? TileXR::TileXRCcuSignalWaitRole::Signal : TileXR::TileXRCcuSignalWaitRole::Wait; +} + +uint32_t DefaultSignalWaitInstructionCount(TileXR::TileXRCcuSignalWaitRole role) +{ + switch (role) { + case TileXR::TileXRCcuSignalWaitRole::Signal: + return 5U; + case TileXR::TileXRCcuSignalWaitRole::Wait: + return 5U; + case TileXR::TileXRCcuSignalWaitRole::SignalAndWait: + return 6U; + default: + return 0U; + } +} + +TileXR::TileXRCcuSignalWaitRequest MakeSignalWaitRequest( + int rank, + int rankSize, + const TileXRDirectCcuPrepareOptions& options) +{ + TileXR::TileXRCcuSignalWaitRequest request {}; + request.peerRank = rankSize == 2 ? 1 - rank : (rank + 1) % rankSize; + request.role = SignalWaitRoleForRank(rank); + request.overrideBarrierMode = BarrierSmokeEnabled(); + request.barrierMode = options.barrierMode; + request.syncInstructionCount = std::getenv("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT") == nullptr ? + 0U : + options.syncInstructionCount; + request.missionStartId = options.missionStartId; + request.instructionStartId = options.instructionStartId; + request.missionInstructionStartId = options.missionInstructionStartId; + request.xnStartId = options.xnStartId; + request.remoteXnStartId = options.remoteXnStartId; + request.remoteXnCount = options.remoteXnCount; + request.ckeStartId = options.ckeStartId; + request.channelStartId = options.channelStartId; + request.localWaitCkeStartId = options.localWaitCkeStartId; + request.localWaitCkeCount = options.localWaitCkeCount; + request.remoteNotifyCkeStartId = options.remoteNotifyCkeStartId; + request.remoteNotifyCkeCount = options.remoteNotifyCkeCount; + request.timeout = static_cast(EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 20)); + request.provider = "tilexr-direct-smoke-probe-signal-wait"; + return request; +} + +TileXRDirectCcuPrepareReport SignalWaitInstallReportFromPlan( + const TileXR::TileXRCcuSignalWaitPlan& plan) +{ + TileXRDirectCcuPrepareReport report {}; + report.pipelineBuilt = !plan.attempt.plan.taskWindows.empty(); + report.installAttempted = plan.attempt.installReport.installAttempted; + report.installSucceeded = plan.attempt.installReport.installSucceeded; + report.submitReady = plan.attempt.providerReport.submitReady; + report.requiredInstallSurfaceCount = plan.attempt.installReport.requiredInstallSurfaceCount; + report.publicVerifiedInstallSurfaceCount = plan.attempt.installReport.publicVerifiedInstallSurfaceCount; + report.missingInstallSurfaceCount = plan.attempt.installReport.missingInstallSurfaceCount; + report.taskCount = static_cast(plan.attempt.plan.taskWindows.size()); + report.submitTaskCount = static_cast(plan.submitTasks.size()); + if (!plan.attempt.providerReport.message.empty()) { + report.message = plan.attempt.providerReport.message; + } else { + report.message = plan.attempt.installReport.message; + } + return report; +} + +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 TileXRDirectCcuGetPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t taskIndex, + TileXRDirectCcuTaskInfo* task) +{ + if (prepared == nullptr || task == nullptr || taskIndex >= prepared->submitTasks.size()) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + *task = prepared->submitTasks[taskIndex]; + return TileXR::TILEXR_SUCCESS; +} + +int TileXRDirectCcuSubmitPrepared( + TileXRDirectCcuPreparedTasksPtr prepared, + void* stream, + TileXRDirectCcuSubmitReport* report) +{ + if (prepared == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + return TileXR::TileXRCcuSubmitPreparedTasks(prepared->submitTasks, stream, nullptr, nullptr, report); +} + +int TileXRDirectCcuSubmitPreparedTask( + TileXRDirectCcuPreparedTasksPtr prepared, + uint32_t taskIndex, + void* stream, + TileXRDirectCcuSubmitReport* report) +{ + if (prepared == nullptr || taskIndex >= prepared->submitTasks.size()) { + if (report != nullptr) { + *report = TileXRDirectCcuSubmitReport {}; + report->taskCount = prepared == nullptr ? 0U : static_cast(prepared->submitTasks.size()); + report->message = "selected direct CCU submit task is missing"; + } + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + std::vector selected {prepared->submitTasks[taskIndex]}; + return TileXR::TileXRCcuSubmitPreparedTasks(selected, stream, nullptr, nullptr, report); +} + +int TileXRDirectCcuDestroyPrepared(TileXRDirectCcuPreparedTasksPtr prepared) +{ + if (prepared == nullptr) { + return TileXR::TILEXR_SUCCESS; + } + return TileXR::TileXRCcuReleaseDirectInstallAttemptResources(*prepared); +} + +int TileXRCommReadDirectCcuInstructions( + DirectCcuSmokeContext* context, + uint8_t dieId, + uint16_t instructionStartId, + uint32_t instructionCount, + TileXRDirectCcuInstructionWords* instructions, + TileXRDirectCcuInstructionReadbackReport* report) +{ + if (context == nullptr || instructions == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + return context->executor.ReadDirectCcuInstructionsForDebug( + context->session, + dieId, + instructionStartId, + instructions, + instructionCount, + sizeof(TileXRDirectCcuInstructionWords), + report); +} + +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 {}; + report->message = "invalid TILEXR_CCU_DIRECT_SMOKE_SUBMIT_TASK_SELECTOR"; + } + 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; + report->message = "selected direct CCU submit task is missing"; + } + 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=" << static_cast(options.repositoryInstallOptions.window) + << " repositoryInstallDataLenMode=" << static_cast(options.repositoryInstallOptions.dataLenMode) + << " repositoryMemoryAllocMode=" << static_cast(options.repositoryMemoryAllocMode) + << " installOrder=" << static_cast(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 << "\"" + << 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; +} + +uint16_t LoadLe16(const uint8_t* data, size_t index) +{ + return static_cast(data[index * 2U]) | + static_cast(static_cast(data[index * 2U + 1U]) << 8U); +} + +bool ReadMissionContextAtEnd( + DirectCcuSmokeContext* context, + const TileXRDirectCcuTaskInfo& task, + const char* label) +{ + if (context == nullptr || label == nullptr) { + return false; + } + TileXR::TileXRCcuDriverAdapter adapter; + TileXR::TileXRCcuDriverAdapterReport adapterReport; + int ret = context->session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << label << " missionCtxRead ret=" << ret + << " message=\"" << adapterReport.message << "\"" + << std::endl; + return false; + } + + uint8_t raw[TileXR::TILEXR_CCU_DATA_ARRAY_SLOT_BYTES] = {}; + ret = adapter.ReadMissionContext( + task.dieId, + task.missionId, + raw, + sizeof(raw), + &adapterReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << label << " missionCtxRead ret=" << ret + << " opcode=" << adapterReport.opcode + << " driverRet=" << adapterReport.driverRet + << " opRet=" << adapterReport.opRet + << " message=\"" << adapterReport.message << "\"" + << std::endl; + return false; + } + + const uint16_t part4 = LoadLe16(raw, 4); + const uint16_t part5 = LoadLe16(raw, 5); + const uint16_t part6 = LoadLe16(raw, 6); + const uint16_t currentIns = static_cast(((part5 & 0x1fU) << 11U) | ((part4 >> 5U) & 0x7ffU)); + const uint16_t endIns = static_cast(((part6 & 0x1fU) << 11U) | ((part5 >> 5U) & 0x7ffU)); + const bool atEnd = currentIns == endIns; + std::cerr << label << " missionCtxAtEnd=" << (atEnd ? 1 : 0) + << " currentIns=" << currentIns + << " endIns=" << endIns + << std::endl; + return atEnd; +} + +void PrintMissionContext( + DirectCcuSmokeContext* context, + const TileXRDirectCcuTaskInfo& task, + const char* label) +{ + if (context == nullptr || label == nullptr) { + return; + } + TileXR::TileXRCcuDriverAdapter adapter; + TileXR::TileXRCcuDriverAdapterReport adapterReport; + int ret = context->session.CreateDriverAdapter(&adapter, &adapterReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << label << " missionCtxRead ret=" << ret + << " message=\"" << adapterReport.message << "\"" + << std::endl; + return; + } + + uint8_t raw[TileXR::TILEXR_CCU_DATA_ARRAY_SLOT_BYTES] = {}; + ret = adapter.ReadMissionContext( + task.dieId, + task.missionId, + raw, + sizeof(raw), + &adapterReport); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << label << " missionCtxRead ret=" << ret + << " opcode=" << adapterReport.opcode + << " driverRet=" << adapterReport.driverRet + << " opRet=" << adapterReport.opRet + << " message=\"" << adapterReport.message << "\"" + << std::endl; + return; + } + + const uint16_t part2 = LoadLe16(raw, 2); + const uint16_t part3 = LoadLe16(raw, 3); + const uint16_t part4 = LoadLe16(raw, 4); + const uint16_t part5 = LoadLe16(raw, 5); + const uint16_t part6 = LoadLe16(raw, 6); + const uint16_t part7 = LoadLe16(raw, 7); + const uint16_t status = static_cast(((part3 & 0x7U) << 13U) | ((part2 >> 3U) & 0x1fffU)); + const uint16_t currentIns = static_cast(((part5 & 0x1fU) << 11U) | ((part4 >> 5U) & 0x7ffU)); + const uint16_t endIns = static_cast(((part6 & 0x1fU) << 11U) | ((part5 >> 5U) & 0x7ffU)); + const uint16_t startIns = static_cast(((part7 & 0x1fU) << 11U) | ((part6 >> 5U) & 0x7ffU)); + const uint16_t missionVld = static_cast((part7 >> 6U) & 0x1U); + + std::cerr << label << " missionCtx" + << " dieId=" << static_cast(task.dieId) + << " missionId=" << static_cast(task.missionId) + << " status=0x" << std::hex << status + << " currentIns=" << std::dec << currentIns + << " startIns=" << startIns + << " endIns=" << endIns + << " missionVld=" << missionVld + << " rawWords="; + for (size_t i = 0; i < sizeof(raw) / sizeof(uint64_t); ++i) { + uint64_t word = 0; + std::memcpy(&word, raw + i * sizeof(uint64_t), sizeof(word)); + if (i != 0) { + std::cerr << ","; + } + std::cerr << "0x" << std::hex << word; + } + std::cerr << std::dec << std::endl; +} + +void PrintCcuResourceState( + DirectCcuSmokeContext* context, + uint8_t dieId, + const TileXRDirectCcuPrepareOptions& options, + const char* label, + uint32_t resourceCount = 3U, + uint32_t extraCkeStartId = 0U, + uint32_t extraCkeCount = 0U) +{ + if (context == nullptr || label == nullptr || resourceCount == 0) { + return; + } + TileXR::TileXRCcuDriverAdapter adapter; + TileXR::TileXRCcuDriverAdapterReport report; + int ret = context->session.CreateDriverAdapter(&adapter, &report); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << label << " resourceState adapterRet=" << ret + << " message=\"" << report.message << "\"" << std::endl; + return; + } + + std::vector localXn(resourceCount, 0); + std::vector remoteXn(resourceCount, 0); + std::vector localWaitCke(resourceCount, 0); + std::vector remoteNotifyCke(resourceCount, 0); + std::vector extraCke(extraCkeCount, 0); + const uint32_t localXnStartId = options.xnStartId; + const uint32_t remoteXnStartId = options.remoteXnStartId; + const uint32_t localWaitCkeStartId = options.localWaitCkeStartId; + const uint32_t remoteNotifyCkeStartId = options.remoteNotifyCkeStartId; + const int localXnRet = adapter.ReadXnRange( + dieId, localXnStartId, localXn.data(), resourceCount, &report); + const int remoteXnRet = adapter.ReadXnRange( + dieId, remoteXnStartId, remoteXn.data(), resourceCount, &report); + const int localCkeRet = adapter.ReadCkeRange( + dieId, localWaitCkeStartId, localWaitCke.data(), resourceCount, &report); + const int remoteCkeRet = adapter.ReadCkeRange( + dieId, remoteNotifyCkeStartId, remoteNotifyCke.data(), resourceCount, &report); + const int extraCkeRet = extraCkeCount == 0U ? TileXR::TILEXR_SUCCESS : + adapter.ReadCkeRange(dieId, extraCkeStartId, extraCke.data(), extraCkeCount, &report); + + const auto values = [](const std::vector& data) { + std::ostringstream out; + for (size_t i = 0; i < data.size(); ++i) { + if (i != 0) { + out << ","; + } + out << "0x" << std::hex << data[i]; + } + return out.str(); + }; + + std::cerr << label << " resourceState" + << " resourceCount=" << resourceCount + << " localXnStartId=" << localXnStartId + << " localXnRet=" << localXnRet + << " localXn=" << values(localXn) + << " remoteXnStartId=" << remoteXnStartId + << " remoteXnRet=" << remoteXnRet + << " remoteXn=" << values(remoteXn) + << " localWaitCkeStartId=" << localWaitCkeStartId + << " localCkeRet=" << localCkeRet + << " localCke=" << values(localWaitCke) + << " remoteNotifyCkeStartId=" << remoteNotifyCkeStartId + << " remoteCkeRet=" << remoteCkeRet + << " remoteCke=" << values(remoteNotifyCke) + << " extraCkeStartId=" << extraCkeStartId + << " extraCkeRet=" << extraCkeRet + << " extraCke=" << values(extraCke) + << std::endl; +} + +int ReadAndValidatePeerLoopMarker( + DirectCcuSmokeContext* context, + uint8_t dieId, + uint32_t markerXnId, + int rank, + int peerRank, + int loopIndex, + uint32_t routeIndex, + uint32_t channelId, + uint32_t ckeId, + uint64_t expectedPeerLoopMarker) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXR::TileXRCcuDriverAdapter adapter; + TileXR::TileXRCcuDriverAdapterReport report; + int readRet = context->session.CreateDriverAdapter(&adapter, &report); + uint64_t peerLoopMarker = 0; + if (readRet == TileXR::TILEXR_SUCCESS) { + readRet = adapter.ReadXnRange(dieId, markerXnId, &peerLoopMarker, 1, &report); + } + const bool matched = readRet == TileXR::TILEXR_SUCCESS && + peerLoopMarker == expectedPeerLoopMarker; + std::cout << "tilexr_ccu_alltoall peerLoopMarker" + << " rank=" << rank + << " peerRank=" << peerRank + << " loopIndex=" << loopIndex + << " route=" << routeIndex + << " channel=" << channelId + << " cke=" << ckeId + << " xnId=" << markerXnId + << " readRet=" << readRet + << " observed=0x" << std::hex << peerLoopMarker + << " expected=0x" << expectedPeerLoopMarker + << std::dec + << " matched=" << (matched ? 1 : 0) + << " message=\"" << report.message << "\"" + << std::endl; + return matched ? TileXR::TILEXR_SUCCESS : TileXR::TILEXR_ERROR_INTERNAL; +} + +void PrintInstructionReadback(DirectCcuSmokeContext* context, TileXRDirectCcuPreparedTasksPtr prepared, uint32_t taskCount) +{ + if (!EnvFlag(kReadbackInstructionsEnv)) { + return; + } + if (context == nullptr) { + std::cout << "tilexr_ccu_direct_smoke instructionReadback ret=" + << TileXR::TILEXR_ERROR_PARA_CHECK_FAIL + << " message=\"missing direct CCU smoke context\"" + << 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( + context, + 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 RankPhaseFileStem(int rank, int phase) +{ + std::string stem = "/rank" + std::to_string(rank); + if (phase >= 0) { + stem += ".phase" + std::to_string(phase); + } + return stem; +} + +std::string SubmitReadinessPath(int rank, int phase = -1) +{ + const char* readyDir = std::getenv(kReadyDirEnv); + if (readyDir == nullptr || readyDir[0] == '\0') { + return {}; + } + return std::string(readyDir) + RankPhaseFileStem(rank, phase) + ".ready"; +} + +bool WriteSubmitReadiness(int rank, bool ready, int phase = -1) +{ + const std::string path = SubmitReadinessPath(rank, phase); + 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, int phase = -1) +{ + if (ready == nullptr) { + return false; + } + const std::string path = SubmitReadinessPath(rank, phase); + 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, int phase = -1) +{ + if (!CollectiveSubmitReadyGateConfigured()) { + return localReady; + } + const bool wrote = WriteSubmitReadiness(rank, localReady, phase); + 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, phase)) { + allSeen = false; + allReady = false; + break; + } + allReady = allReady && peerReady; + } + if (allSeen) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitReady" + << " rank=" << rank + << " phase=" << phase + << " 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 + << " phase=" << phase + << " 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, int phase = -1) +{ + const char* doneDir = std::getenv(kDoneDirEnv); + if (doneDir == nullptr || doneDir[0] == '\0') { + return {}; + } + return std::string(doneDir) + RankPhaseFileStem(rank, phase) + ".done"; +} + +bool WriteSubmitDone(int rank, int result, int phase = -1) +{ + const std::string path = SubmitDonePath(rank, phase); + 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, int phase = -1) +{ + if (result == nullptr) { + return false; + } + const std::string path = SubmitDonePath(rank, phase); + 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, int phase = -1) +{ + if (!CollectiveSubmitDoneGateConfigured()) { + return true; + } + const bool wrote = WriteSubmitDone(rank, localResult, phase); + 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, phase)) { + allSeen = false; + allSucceeded = false; + break; + } + allSucceeded = allSucceeded && peerResult == 0; + } + if (allSeen) { + std::cout << "tilexr_ccu_direct_smoke collectiveSubmitDone" + << " rank=" << rank + << " phase=" << phase + << " localResult=" << localResult + << " allRanksDone=1" + << " allRanksSucceeded=" << (allSucceeded ? 1 : 0) + << std::endl; + return allSucceeded; + } + 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 + << " phase=" << phase + << " localResult=" << localResult + << " allRanksDone=0" + << " timeoutMs=" << timeoutMs + << std::endl; + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +bool WaitForInactiveP2pCcuCopyRank(int rank, int rankSize, int localResult) +{ + return WaitForCollectiveSubmitDone(rank, rankSize, localResult); +} + +int RunInactiveP2pCcuCopyRank(int rank, int peer, int rankSize, P2pCcuCopyState* state, int localResult) +{ + if (state == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + PrintP2pCcuCopySkipped(rank, peer, *state); + if (!WaitForInactiveP2pCcuCopyRank(rank, rankSize, localResult) && localResult == 0) { + return 13; + } + if (localResult != 0 || !ShouldCheckInactiveP2pCcuCopyRank()) { + return localResult; + } + const int checkRet = CheckP2pCcuCopyState(state); + PrintP2pCcuCopy(rank, peer, *state, TileXR::TILEXR_SUCCESS, TileXR::TILEXR_SUCCESS, ACL_SUCCESS); + return checkRet == ACL_SUCCESS ? 0 : 14; +} + +int RunAllToAllCopyPhase( + DirectCcuSmokeContext* context, + int rank, + int rankSize, + int device, + int phase, + AllToAllState* alltoall) +{ + if (context == nullptr || alltoall == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = 1; + options.sqeArgCount = 0; + options.syncInstructionCount = 7; + if (options.gsaStartId == 0) { + options.gsaStartId = 1; + } + + const int peer = 1 - rank; + const bool singleRouteBidirectional = AllToAllSingleRouteBidirectionalEnabled(); + const bool active = singleRouteBidirectional || rank == phase; + const bool submitRequested = EnvFlag(kSubmitEnv); + + std::cout << "tilexr_ccu_alltoall phase" + << " rank=" << rank + << " phase=" << phase + << " direction=" << (singleRouteBidirectional ? "LocalToRemote" : "RemoteToLocal") + << " singleRouteBidirectional=" << (singleRouteBidirectional ? 1 : 0) + << std::endl; + + TileXR::TileXRCcuDirectInstallAttempt attempt; + TileXRDirectCcuPreparedTasksPtr prepared = &attempt; + TileXRDirectCcuPrepareReport installReport; + const int prepareRet = alltoall->initRet != ACL_SUCCESS ? + alltoall->initRet : + context->planner.PrepareDirectCcuMemoryCopyInstallAttempt( + context->session, + options, + reinterpret_cast(alltoall->source.ptr), + reinterpret_cast(alltoall->destination.ptr), + alltoall->bytes, + static_cast(peer), + singleRouteBidirectional ? + TileXR::TileXRCcuMemoryCopyDirection::LocalToRemote : + TileXR::TileXRCcuMemoryCopyDirection::RemoteToLocal, + prepared, + &installReport); + PrintInstallReport("tilexr_ccu_alltoall prepare", prepareRet, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(context, prepared, installReport.submitTaskCount); + + int finalRet = 0; + if (!active) { + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (submitRequested) { + const bool phaseReady = WaitForCollectiveSubmitReadiness(rank, rankSize, installReport.submitReady, phase); + finalRet = phaseReady ? 0 : 13; + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet, phase) && finalRet == 0) { + finalRet = 13; + } + } + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet != TileXR::TILEXR_SUCCESS && finalRet == 0 ? 11 : finalRet; + } + + const bool collectiveSubmitReady = submitRequested ? + WaitForCollectiveSubmitReadiness( + rank, + rankSize, + prepareRet == TileXR::TILEXR_SUCCESS && installReport.submitReady, + phase) : + false; + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (submitRequested && !collectiveSubmitReady && CollectiveSubmitReadyGateConfigured()) { + std::cout << "tilexr_ccu_alltoall 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_alltoall submit skipped reason=\"prepare did not reach submitReady\"" << std::endl; + finalRet = 6; + } else if (submitRequested) { + aclrtStream stream = nullptr; + int streamRet = aclrtCreateStream(&stream); + if (streamRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_alltoall aclrtCreateStream ret=" << streamRet << std::endl; + finalRet = 7; + } else { + bool skipStreamDestroy = false; + TileXRDirectCcuSubmitReport submitReport; + const auto submitBegin = std::chrono::steady_clock::now(); + const int submitRet = TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport); + const auto submitEnd = std::chrono::steady_clock::now(); + PrintSubmitReport("tilexr_ccu_alltoall submit", submitRet, submitReport); + const auto syncBegin = std::chrono::steady_clock::now(); + const int syncTimeoutMs = std::max(1, EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 6000)); + TraceLifecycle("before alltoall aclrtSynchronizeStreamWithTimeout"); + const int syncRet = aclrtSynchronizeStreamWithTimeout(stream, syncTimeoutMs); + TraceLifecycle("after alltoall aclrtSynchronizeStreamWithTimeout"); + const auto syncEnd = std::chrono::steady_clock::now(); + std::cout << "tilexr_ccu_alltoall timing" + << " rank=" << rank + << " phase=" << phase + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " syncTimeoutMs=" << syncTimeoutMs + << " submitMs=" + << std::chrono::duration_cast(submitEnd - submitBegin).count() + << " syncMs=" + << std::chrono::duration_cast(syncEnd - syncBegin).count() + << std::endl; + if (syncRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_alltoall aclrtSynchronizeStreamWithTimeout ret=" << syncRet + << " timeoutMs=" << syncTimeoutMs << std::endl; + bool missionAtEnd = false; + if (!attempt.submitTasks.empty()) { + PrintMissionContext(context, attempt.submitTasks.front(), "tilexr_ccu_alltoall"); + missionAtEnd = ReadMissionContextAtEnd( + context, + attempt.submitTasks.front(), + "tilexr_ccu_alltoall"); + } + PrintCcuResourceState( + context, + attempt.submitTasks.empty() ? 0 : attempt.submitTasks.front().dieId, + options, + "tilexr_ccu_alltoall"); + if (missionAtEnd) { + std::cout << "tilexr_ccu_alltoall streamTimeoutAtMissionEnd=1" + << " rank=" << rank + << " reason=\"continuing to device buffer validation\"" + << std::endl; + skipStreamDestroy = true; + } else { + finalRet = 8; + } + } else if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet, phase) && finalRet == 0) { + finalRet = 13; + } + if (skipStreamDestroy) { + std::cout << "tilexr_ccu_alltoall skipDestroyStream=1" + << " rank=" << rank + << " reason=\"stream timeout after mission reached end\"" + << std::endl; + } else { + aclrtDestroyStream(stream); + } + } + } + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet != TileXR::TILEXR_SUCCESS && finalRet == 0 ? 11 : finalRet; +} + +void PrintAllToAllResult(int rank, int loopIndex, int finalRet, const AllToAllState& alltoall) +{ + if (finalRet == 0) { + std::cout << "tilexr_ccu_alltoall result passed=1" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << finalRet + << " readRet=" << alltoall.readRet + << " mismatches=" << alltoall.mismatchCount + << std::endl; + } else { + const size_t chunkBytes = alltoall.chunkBytes == 0 ? alltoall.bytes : alltoall.chunkBytes; + const size_t sourceRank = chunkBytes == 0 ? 0 : alltoall.firstMismatchOffset / chunkBytes; + const size_t chunkOffset = chunkBytes == 0 ? 0 : alltoall.firstMismatchOffset % chunkBytes; + std::cout << "tilexr_ccu_alltoall result passed=0" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << finalRet + << " readRet=" << alltoall.readRet + << " mismatches=" << alltoall.mismatchCount + << " sourceRank=" << sourceRank + << " chunkOffset=" << chunkOffset + << " globalOffset=" << alltoall.firstMismatchOffset + << " firstMismatchOffset=" << alltoall.firstMismatchOffset + << " lastMismatchOffset=" << alltoall.lastMismatchOffset + << " firstMismatchObserved=0x" << std::hex << alltoall.firstMismatchObserved + << " firstMismatchExpected=0x" << alltoall.firstMismatchExpected + << std::dec + << " mismatchedBlocks=" << alltoall.mismatchedBlockCount + << " firstMismatchedBlock=" << alltoall.firstMismatchedBlock + << " lastMismatchedBlock=" << alltoall.lastMismatchedBlock + << std::endl; + } +} + +void MaybeFastExitAfterAllToAllRun(int finalRet) +{ + if (ShouldFastExitAfterRun()) { + std::cout << "tilexr_ccu_alltoall fastExitAfterRun=1" + << " ret=" << finalRet + << " reason=\"skipping prepared-task cleanup to isolate cleanup hangs\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } +} + +bool MeshPreparedIdentityMatches( + const TileXR::TileXRCcuDirectInstallAttempt& attempt, + const TileXR::TileXRCcuTask& stableTask, + const std::vector& stableResources) +{ + if (attempt.submitTasks.size() != 1U || attempt.plan.syncResources.size() != stableResources.size()) { + return false; + } + const auto& task = attempt.submitTasks.front(); + if (task.dieId != stableTask.dieId || task.missionId != stableTask.missionId || + task.key != stableTask.key || task.instStartId != stableTask.instStartId || + task.instCnt != stableTask.instCnt || task.argSize != stableTask.argSize) { + return false; + } + for (size_t i = 0; i < stableResources.size(); ++i) { + const auto& current = attempt.plan.syncResources[i]; + const auto& stable = stableResources[i]; + if (current.localXn != stable.localXn || current.remoteXn != stable.remoteXn || + current.notifyCke != stable.notifyCke || current.localWaitCke != stable.localWaitCke || + current.sourceCke != stable.sourceCke || current.channelId != stable.channelId) { + return false; + } + } + return true; +} + +int RunAllToAllMeshLongMissionSmokeForRank( + DirectCcuSmokeContext* context, + int rank, + int rankSize, + int device) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (rankSize < 2 || rankSize > 64) { + std::cout << "tilexr_ccu_alltoall skipped rankSize=" << rankSize + << " reason=\"direct CCU alltoall mesh requires 2..64 ranks\"" << std::endl; + return 0; + } + + const int loopCount = AllToAllLoopCountFromEnv(); + AllToAllState alltoall; + alltoall.initRet = InitAllToAllMeshState(rank, rankSize, &alltoall); + if (loopCount == 0 && alltoall.initRet == ACL_SUCCESS) { + alltoall.initRet = TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = static_cast(rankSize - 1); + options.sqeArgCount = TILEXR_DIRECT_CCU_SQE_ARGS_LEN; + if (std::getenv("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT") == nullptr) { + const uint32_t peerCount = static_cast(rankSize - 1); + const uint32_t completionCkeCount = (peerCount + 15U) / 16U; + const uint64_t blockCount = alltoall.chunkBytes / TileXR::TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + const uint64_t copyPerBlock = peerCount * 6ULL + 9ULL + completionCkeCount; + options.syncInstructionCount = static_cast( + 3ULL + peerCount * 3ULL + + blockCount * copyPerBlock + + peerCount * 2ULL + 1ULL); + } + if (options.gsaStartId == 0) { + options.gsaStartId = 1; + } + std::cout << "tilexr_ccu_alltoall config" + << " rank=" << rank + << " rankSize=" << rankSize + << " chunkBytes=" << alltoall.chunkBytes + << " bytes=" << alltoall.bytes + << " loopCount=" << loopCount + << " resourceCount=" << (rankSize - 1) + << " mesh=1" + << " longMission=1" + << std::endl; + PrintConfig(options, rankSize); + + TileXR::TileXRCcuDirectInstallAttempt attempt; + TileXRDirectCcuPreparedTasksPtr prepared = &attempt; + TileXRDirectCcuPrepareReport installReport; + const int prepareRet = alltoall.initRet != ACL_SUCCESS ? + alltoall.initRet : + context->planner.PrepareDirectCcuAllToAllMeshInstallAttempt( + context->session, + options, + reinterpret_cast(alltoall.source.ptr), + reinterpret_cast(alltoall.destination.ptr), + alltoall.chunkBytes, + prepared, + &installReport); + PrintInstallReport("tilexr_ccu_alltoall prepare", prepareRet, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(context, prepared, installReport.submitTaskCount); + + int finalRet = 0; + const bool submitRequested = EnvFlag(kSubmitEnv); + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (attempt.submitTasks.size() != 1U || attempt.submitTasks.front().argSize != + TILEXR_DIRECT_CCU_SQE_ARGS_LEN || + attempt.plan.syncResources.size() != static_cast(rankSize - 1)) { + std::cerr << "tilexr_ccu_alltoall invalidMeshPreparedTask" + << " rank=" << rank + << " taskCount=" << attempt.submitTasks.size() + << " resourceCount=" << attempt.plan.syncResources.size() + << std::endl; + finalRet = 6; + } else if (submitRequested && !installReport.submitReady) { + std::cout << "tilexr_ccu_alltoall submit skipped reason=\"prepare did not reach submitReady\"" + << std::endl; + } else if (submitRequested) { + const TileXR::TileXRCcuTask stableTask = attempt.submitTasks.front(); + const auto stableResources = attempt.plan.syncResources; + aclrtStream stream = nullptr; + const int streamRet = aclrtCreateStream(&stream); + if (streamRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_alltoall aclrtCreateStream ret=" << streamRet << std::endl; + finalRet = 7; + } else { + bool skipStreamDestroy = false; + const int syncTimeoutMs = std::max(1, EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 6000)); + for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex) { + const int resetRet = ResetAllToAllMeshStateForLoop(rank, loopIndex, &alltoall); + const bool ready = WaitForCollectiveSubmitReadiness( + rank, + rankSize, + resetRet == ACL_SUCCESS && installReport.submitReady, + loopIndex); + if (resetRet != ACL_SUCCESS) { + finalRet = 14; + } else if (!ready) { + finalRet = 13; + } + + if (finalRet == 0) { + TileXRDirectCcuSubmitReport submitReport; + const int submitRet = TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport); + PrintSubmitReport("tilexr_ccu_alltoall submit", submitRet, submitReport); + const int syncRet = aclrtSynchronizeStreamWithTimeout(stream, syncTimeoutMs); + std::cout << "tilexr_ccu_alltoall timing" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " mesh=1" + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " syncTimeoutMs=" << syncTimeoutMs + << std::endl; + if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } else if (syncRet != ACL_SUCCESS) { + finalRet = 8; + skipStreamDestroy = true; + } + } + + if (finalRet == 0 && CheckAllToAllState(&alltoall) != ACL_SUCCESS) { + finalRet = 14; + } + if (finalRet == 0 && !MeshPreparedIdentityMatches(attempt, stableTask, stableResources)) { + finalRet = 16; + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet, loopIndex) && finalRet == 0) { + finalRet = 13; + } + PrintAllToAllResult(rank, loopIndex, finalRet, alltoall); + if (finalRet != 0) { + std::cerr << "tilexr_ccu_alltoall loopFailure" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << finalRet + << " resourceCount=" << (rankSize - 1) + << " selfCopyCompletionCke=" << attempt.plan.syncResources[0].localWaitCke + << std::endl; + PrintMissionContext(context, attempt.submitTasks.front(), "tilexr_ccu_alltoall"); + PrintCcuResourceState( + context, + attempt.submitTasks.front().dieId, + options, + "tilexr_ccu_alltoall", + static_cast(rankSize - 1), + attempt.allocation.sourceCke.startId, + attempt.allocation.sourceCke.num); + break; + } + std::cout << "tilexr_ccu_alltoall stableResources=1" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " missionId=" << static_cast(stableTask.missionId) + << " instStartId=" << stableTask.instStartId + << " instCnt=" << stableTask.instCnt + << std::endl; + } + if (skipStreamDestroy) { + std::cout << "tilexr_ccu_alltoall skipDestroyStream=1 rank=" << rank << std::endl; + } else { + aclrtDestroyStream(stream); + } + } + } + + MaybeFastExitAfterAllToAllRun(finalRet); + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet != TileXR::TILEXR_SUCCESS && finalRet == 0 ? 11 : finalRet; +} + +int RunAllToAllLongMissionSmokeForRank(DirectCcuSmokeContext* context, int rank, int rankSize, int device) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AllToAllMeshSmokeEnabled()) { + return RunAllToAllMeshLongMissionSmokeForRank(context, rank, rankSize, device); + } + if (rankSize != 2) { + std::cout << "tilexr_ccu_alltoall skipped rankSize=" << rankSize + << " reason=\"direct CCU alltoall MVP requires two ranks\"" << std::endl; + return 0; + } + + const int peer = 1 - rank; + const int loopCount = AllToAllLoopCountFromEnv(); + AllToAllState alltoall; + alltoall.initRet = InitAllToAllState(rank, peer, &alltoall); + if (loopCount == 0 && alltoall.initRet == ACL_SUCCESS) { + alltoall.initRet = TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = 3; + options.sqeArgCount = TILEXR_DIRECT_CCU_SQE_ARGS_LEN; + const size_t blockCount = alltoall.bytes / TileXR::TILEXR_CCU_ALLTOALL_BLOCK_BYTES; + if (std::getenv("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT") == nullptr) { + options.syncInstructionCount = static_cast(5U + blockCount * 7U); + } + if (options.gsaStartId == 0) { + options.gsaStartId = 1; + } + + std::cout << "tilexr_ccu_alltoall config" + << " rank=" << rank + << " peer=" << peer + << " bytes=" << alltoall.bytes + << " loopCount=" << loopCount + << " memSlicePerLoop=" << AllToAllMemSlicePerLoopFromEnv() + << " blockCount=" << blockCount + << " longMission=1" + << " preSync=1" + << " postSync=0" + << " finish=0" + << std::endl; + PrintConfig(options, rankSize); + + TileXR::TileXRCcuDirectInstallAttempt attempt; + TileXRDirectCcuPreparedTasksPtr prepared = &attempt; + TileXRDirectCcuPrepareReport installReport; + const int prepareRet = alltoall.initRet != ACL_SUCCESS ? + alltoall.initRet : + context->planner.PrepareDirectCcuAllToAll2RankInstallAttempt( + context->session, + options, + reinterpret_cast(alltoall.source.ptr), + reinterpret_cast(alltoall.destination.ptr), + alltoall.bytes, + static_cast(peer), + prepared, + &installReport); + PrintInstallReport("tilexr_ccu_alltoall prepare", prepareRet, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(context, prepared, installReport.submitTaskCount); + + int finalRet = 0; + const bool submitRequested = EnvFlag(kSubmitEnv); + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (attempt.submitTasks.empty() || + attempt.submitTasks.front().argSize != TILEXR_DIRECT_CCU_SQE_ARGS_LEN) { + std::cerr << "tilexr_ccu_alltoall invalidPreparedTask" + << " rank=" << rank + << " taskCount=" << attempt.submitTasks.size() + << " argSize=" << (attempt.submitTasks.empty() ? 0 : attempt.submitTasks.front().argSize) + << std::endl; + finalRet = 6; + } else if (submitRequested && !installReport.submitReady) { + std::cout << "tilexr_ccu_alltoall 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_alltoall aclrtCreateStream ret=" << streamRet << std::endl; + finalRet = 7; + } else { + bool skipStreamDestroy = false; + int lastLoopIndex = -1; + const int syncTimeoutMs = std::max(1, EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 6000)); + for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex) { + lastLoopIndex = loopIndex; + const uint64_t localLoopMarker = BuildAllToAllLoopMarker(rank, loopIndex); + attempt.submitTasks.front().args[0] = localLoopMarker; + const bool collectiveSubmitReady = WaitForCollectiveSubmitReadiness( + rank, + rankSize, + installReport.submitReady, + loopIndex); + if (!collectiveSubmitReady) { + finalRet = 13; + } + + if (finalRet == 0) { + TileXRDirectCcuSubmitReport submitReport; + const auto submitBegin = std::chrono::steady_clock::now(); + const int submitRet = TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport); + const auto submitEnd = std::chrono::steady_clock::now(); + PrintSubmitReport("tilexr_ccu_alltoall submit", submitRet, submitReport); + const auto syncBegin = std::chrono::steady_clock::now(); + TraceLifecycle("before alltoall long mission aclrtSynchronizeStreamWithTimeout"); + const int syncRet = aclrtSynchronizeStreamWithTimeout(stream, syncTimeoutMs); + TraceLifecycle("after alltoall long mission aclrtSynchronizeStreamWithTimeout"); + const auto syncEnd = std::chrono::steady_clock::now(); + std::cout << "tilexr_ccu_alltoall timing" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " longMission=1" + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " syncTimeoutMs=" << syncTimeoutMs + << " submitMs=" + << std::chrono::duration_cast(submitEnd - submitBegin).count() + << " syncMs=" + << std::chrono::duration_cast(syncEnd - syncBegin).count() + << std::endl; + if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } else if (syncRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_alltoall aclrtSynchronizeStreamWithTimeout" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << syncRet + << " timeoutMs=" << syncTimeoutMs << std::endl; + finalRet = 8; + skipStreamDestroy = true; + } + } + + if (finalRet == 0) { + const int markerRet = ReadAndValidatePeerLoopMarker( + context, + attempt.submitTasks.front().dieId, + attempt.plan.syncResources[0].remoteXn, + rank, + peer, + loopIndex, + 0U, + attempt.plan.syncResources[0].channelId, + attempt.plan.syncResources[0].localWaitCke, + BuildAllToAllLoopMarker(peer, loopIndex)); + if (markerRet != TileXR::TILEXR_SUCCESS) { + finalRet = 15; + } + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet, loopIndex) && finalRet == 0) { + finalRet = 13; + } + std::cout << "tilexr_ccu_alltoall loopResult" + << " passed=" << (finalRet == 0 ? 1 : 0) + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << finalRet + << " dataCheckDeferred=1" + << std::endl; + if (finalRet != 0) { + std::cerr << "tilexr_ccu_alltoall loopFailure" + << " rank=" << rank + << " loopIndex=" << loopIndex + << " ret=" << finalRet << std::endl; + PrintMissionContext(context, attempt.submitTasks.front(), "tilexr_ccu_alltoall"); + PrintCcuResourceState( + context, + attempt.submitTasks.front().dieId, + options, + "tilexr_ccu_alltoall"); + break; + } + } + if (finalRet == 0 && CheckAllToAllState(&alltoall) != ACL_SUCCESS) { + finalRet = 14; + } + PrintAllToAllResult(rank, lastLoopIndex, finalRet, alltoall); + if (skipStreamDestroy) { + std::cout << "tilexr_ccu_alltoall skipDestroyStream=1" + << " rank=" << rank + << " reason=\"stream timeout after mission reached end\"" + << std::endl; + } else { + aclrtDestroyStream(stream); + } + } + } + + if (!submitRequested && finalRet == 0) { + const int checkRet = CheckAllToAllState(&alltoall); + if (checkRet != ACL_SUCCESS) { + finalRet = 14; + } + PrintAllToAllResult(rank, -1, finalRet, alltoall); + } + MaybeFastExitAfterAllToAllRun(finalRet); + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet != TileXR::TILEXR_SUCCESS && finalRet == 0 ? 11 : finalRet; +} + +int RunAllToAllSmokeForRank(DirectCcuSmokeContext* context, int rank, int rankSize, int device) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (AllToAllMeshSmokeEnabled() || AllToAllLongMissionEnabled()) { + return RunAllToAllLongMissionSmokeForRank(context, rank, rankSize, device); + } + if (rankSize != 2) { + std::cout << "tilexr_ccu_alltoall skipped rankSize=" << rankSize + << " reason=\"direct CCU alltoall MVP requires two ranks\"" << std::endl; + return 0; + } + + const int peer = 1 - rank; + AllToAllState alltoall; + alltoall.initRet = InitAllToAllState(rank, peer, &alltoall); + + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = 1; + options.sqeArgCount = 0; + options.syncInstructionCount = 7; + if (options.gsaStartId == 0) { + options.gsaStartId = 1; + } + + std::cout << "tilexr_ccu_alltoall config" + << " rank=" << rank + << " peer=" << peer + << " bytes=" << alltoall.bytes + << " memSlicePerLoop=" << AllToAllMemSlicePerLoopFromEnv() + << " blockCount=64" + << " hostPhases=2" + << std::endl; + PrintConfig(options, rankSize); + + int finalRet = alltoall.initRet == ACL_SUCCESS ? 0 : alltoall.initRet; + const int phaseCount = AllToAllSingleRouteBidirectionalEnabled() ? 1 : 2; + for (int phase = 0; phase < phaseCount && finalRet == 0; ++phase) { + finalRet = RunAllToAllCopyPhase(context, rank, rankSize, device, phase, &alltoall); + } + if (finalRet == 0) { + const int checkRet = CheckAllToAllState(&alltoall); + if (checkRet != ACL_SUCCESS) { + finalRet = 14; + } + } + + PrintAllToAllResult(rank, -1, finalRet, alltoall); + MaybeFastExitAfterAllToAllRun(finalRet); + return finalRet; +} + +int RunSyncXnPingSmokeForRank(DirectCcuSmokeContext* context, int rank, int rankSize, int device) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (rankSize != 2 && rankSize != 4) { + std::cout << "tilexr_ccu_sync_xn_ping skipped rankSize=" << rankSize + << " reason=\"direct CCU SyncXn ping requires two or four ranks\"" << std::endl; + return 0; + } + + const int peerXor = EnvInt(kSyncXnPingPeerXorEnv, 1); + if (peerXor < 1 || peerXor >= rankSize) { + std::cerr << "tilexr_ccu_sync_xn_ping invalid peerXor=" << peerXor + << " rankSize=" << rankSize << std::endl; + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + const int peer = rank ^ peerXor; + AllToAllState routeState; + routeState.initRet = InitAllToAllState(rank, peer, &routeState); + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = 1; + options.sqeArgCount = 0; + options.syncInstructionCount = 2; + + std::cout << "tilexr_ccu_sync_xn_ping config" + << " rank=" << rank + << " peer=" << peer + << " peerXor=" << peerXor + << std::endl; + PrintConfig(options, rankSize); + + TileXR::TileXRCcuDirectInstallAttempt attempt; + TileXRDirectCcuPreparedTasksPtr prepared = &attempt; + TileXRDirectCcuPrepareReport installReport; + const int prepareRet = routeState.initRet != ACL_SUCCESS ? + routeState.initRet : + context->planner.PrepareDirectCcuSyncXnPingInstallAttempt( + context->session, + options, + reinterpret_cast(routeState.source.ptr), + reinterpret_cast(routeState.destination.ptr), + routeState.bytes, + static_cast(peer), + prepared, + &installReport); + PrintInstallReport("tilexr_ccu_sync_xn_ping prepare", prepareRet, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(context, prepared, installReport.submitTaskCount); + + int finalRet = 0; + const bool submitRequested = EnvFlag(kSubmitEnv); + const bool collectiveSubmitReady = submitRequested ? + WaitForCollectiveSubmitReadiness( + rank, + rankSize, + prepareRet == TileXR::TILEXR_SUCCESS && installReport.submitReady) : + false; + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (submitRequested && !collectiveSubmitReady && CollectiveSubmitReadyGateConfigured()) { + std::cout << "tilexr_ccu_sync_xn_ping 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_sync_xn_ping 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_sync_xn_ping aclrtCreateStream ret=" << streamRet << std::endl; + finalRet = 7; + } else { + TileXRDirectCcuSubmitReport submitReport; + const auto submitBegin = std::chrono::steady_clock::now(); + const int submitRet = TileXRDirectCcuSubmitPrepared(prepared, stream, &submitReport); + const auto submitEnd = std::chrono::steady_clock::now(); + PrintSubmitReport("tilexr_ccu_sync_xn_ping submit", submitRet, submitReport); + const auto syncBegin = std::chrono::steady_clock::now(); + const int syncTimeoutMs = std::max(1, EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 6000)); + TraceLifecycle("before SyncXn ping aclrtSynchronizeStreamWithTimeout"); + const int syncRet = aclrtSynchronizeStreamWithTimeout(stream, syncTimeoutMs); + TraceLifecycle("after SyncXn ping aclrtSynchronizeStreamWithTimeout"); + const auto syncEnd = std::chrono::steady_clock::now(); + std::cout << "tilexr_ccu_sync_xn_ping timing" + << " rank=" << rank + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " syncTimeoutMs=" << syncTimeoutMs + << " submitMs=" + << std::chrono::duration_cast(submitEnd - submitBegin).count() + << " syncMs=" + << std::chrono::duration_cast(syncEnd - syncBegin).count() + << std::endl; + if (syncRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_sync_xn_ping aclrtSynchronizeStreamWithTimeout ret=" << syncRet + << " timeoutMs=" << syncTimeoutMs << std::endl; + if (!attempt.submitTasks.empty()) { + PrintMissionContext(context, attempt.submitTasks.front(), "tilexr_ccu_sync_xn_ping"); + } + PrintCcuResourceState( + context, + attempt.submitTasks.empty() ? 0 : attempt.submitTasks.front().dieId, + options, + "tilexr_ccu_sync_xn_ping", + 1U); + finalRet = 8; + } else if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet) && finalRet == 0) { + finalRet = 13; + } + aclrtDestroyStream(stream); + } + } + + if (finalRet == 0) { + std::cout << "tilexr_ccu_sync_xn_ping result passed=1" + << " rank=" << rank + << " ret=" << finalRet + << std::endl; + } else { + std::cout << "tilexr_ccu_sync_xn_ping result passed=0" + << " rank=" << rank + << " ret=" << finalRet + << std::endl; + } + if (ShouldFastExitAfterRun()) { + std::cout << "tilexr_ccu_sync_xn_ping fastExitAfterRun=1" + << " ret=" << finalRet + << " reason=\"skipping prepared-task cleanup to isolate cleanup hangs\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } + const int destroyRet = TileXRDirectCcuDestroyPrepared(prepared); + return destroyRet != TileXR::TILEXR_SUCCESS && finalRet == 0 ? 11 : finalRet; +} + +int RunSignalWaitSmokeForRank(DirectCcuSmokeContext* context, int rank, int rankSize, int device) +{ + if (context == nullptr) { + return TileXR::TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + options.syncResourceCount = 1; + options.sqeArgCount = 0; + const TileXR::TileXRCcuSignalWaitRole role = SignalWaitRoleForRank(rank); + if (std::getenv("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT") == nullptr) { + options.syncInstructionCount = DefaultSignalWaitInstructionCount(role); + } + PrintConfig(options, rankSize); + + const TileXR::TileXRCcuSignalWaitRequest request = MakeSignalWaitRequest(rank, rankSize, options); + std::cout << "tilexr_ccu_signal_wait config" + << " rank=" << rank + << " peer=" << request.peerRank + << " role=" << SignalWaitRoleName(request.role) + << " barrier=" << (BarrierSmokeEnabled() ? 1 : 0) + << std::endl; + + TileXR::TileXRCcuSignalWaitPlan plan; + const int prepareRet = context->backend.PrepareSignalWait(request, &plan); + TileXRDirectCcuPrepareReport installReport = SignalWaitInstallReportFromPlan(plan); + PrintInstallReport("tilexr_ccu_signal_wait prepare", prepareRet, installReport); + PrintPreparedTasks(&plan.attempt, installReport.submitTaskCount); + + int finalRet = 0; + const bool submitRequested = EnvFlag(kSubmitEnv); + const bool collectiveSubmitReady = submitRequested ? + WaitForCollectiveSubmitReadiness( + rank, + rankSize, + prepareRet == TileXR::TILEXR_SUCCESS && installReport.submitReady && plan.ready) : + false; + if (prepareRet != TileXR::TILEXR_SUCCESS) { + finalRet = 6; + } else if (submitRequested && !collectiveSubmitReady && CollectiveSubmitReadyGateConfigured()) { + std::cout << "tilexr_ccu_signal_wait submit skipped reason=\"collective submitReady gate did not pass\"" + << " localSubmitReady=" << (installReport.submitReady ? 1 : 0) + << std::endl; + } else if (submitRequested && (!installReport.submitReady || !plan.ready)) { + std::cout << "tilexr_ccu_signal_wait 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_signal_wait 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_signal_wait 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 int submitRet = context->backend.SubmitSignalWait(plan, stream, &submitReport); + const auto submitEnd = std::chrono::steady_clock::now(); + PrintSubmitReport("tilexr_ccu_signal_wait submit", submitRet, submitReport); + const auto syncBegin = std::chrono::steady_clock::now(); + const int syncTimeoutMs = std::max(1, EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 6000)); + TraceLifecycle("before signal/wait aclrtSynchronizeStreamWithTimeout"); + const int syncRet = aclrtSynchronizeStreamWithTimeout(stream, syncTimeoutMs); + TraceLifecycle("after signal/wait aclrtSynchronizeStreamWithTimeout"); + const auto syncEnd = std::chrono::steady_clock::now(); + std::cout << "tilexr_ccu_signal_wait timing" + << " rank=" << rank + << " role=" << SignalWaitRoleName(request.role) + << " preSubmitDelayMs=" << effectiveDelayMs + << " submitRet=" << submitRet + << " syncRet=" << syncRet + << " syncTimeoutMs=" << syncTimeoutMs + << " submitMs=" + << std::chrono::duration_cast(submitEnd - submitBegin).count() + << " syncMs=" + << std::chrono::duration_cast(syncEnd - syncBegin).count() + << std::endl; + if (syncRet != ACL_SUCCESS) { + std::cerr << "tilexr_ccu_signal_wait aclrtSynchronizeStreamWithTimeout ret=" << syncRet + << " timeoutMs=" << syncTimeoutMs << std::endl; + if (!plan.submitTasks.empty()) { + PrintMissionContext(context, plan.submitTasks.front(), "tilexr_ccu_signal_wait"); + } + finalRet = 8; + } else if (submitRet != TileXR::TILEXR_SUCCESS) { + finalRet = 9; + } + if (!WaitForCollectiveSubmitDone(rank, rankSize, finalRet) && finalRet == 0) { + finalRet = 13; + } + aclrtDestroyStream(stream); + } + } + + const bool passed = finalRet == 0; + if (passed) { + std::cout << "tilexr_ccu_signal_wait result passed=1" + << " rank=" << rank + << " role=" << SignalWaitRoleName(request.role) + << " ret=" << finalRet + << std::endl; + } else { + std::cout << "tilexr_ccu_signal_wait result passed=0" + << " rank=" << rank + << " role=" << SignalWaitRoleName(request.role) + << " ret=" << finalRet + << std::endl; + } + if (ShouldFastExitAfterRun()) { + std::cout << "tilexr_ccu_signal_wait fastExitAfterRun=1" + << " ret=" << finalRet + << " reason=\"skipping prepared-task cleanup to isolate cleanup hangs\"" + << std::endl; + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(finalRet); + } + const int releaseRet = TileXR::TileXRCcuReleaseDirectInstallAttemptResources(plan.attempt); + if (releaseRet != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_signal_wait prepared destroy ret=" << releaseRet << std::endl; + finalRet = finalRet == 0 ? 11 : finalRet; + } + return finalRet; +} + +int RunPreparedSmokeForRank(DirectCcuSmokeContext* context, int rank, int rankSize, int device) +{ + if (SyncXnPingSmokeEnabled()) { + return RunSyncXnPingSmokeForRank(context, rank, rankSize, device); + } + if (AllToAllSmokeEnabled()) { + return RunAllToAllSmokeForRank(context, rank, rankSize, device); + } + if (SignalWaitSmokeEnabled() || BarrierSmokeEnabled()) { + return RunSignalWaitSmokeForRank(context, rank, rankSize, device); + } + + TileXRDirectCcuPrepareOptions options = MakePrepareOptions(rank, rankSize, device); + const int peer = rankSize == 2 ? 1 - rank : (rank + 1) % rankSize; + const bool p2pCcuCopyEnabled = EnvFlag(kP2pCcuCopyEnv); + const bool p2pCcuCopyActiveRank = !p2pCcuCopyEnabled || IsP2pCcuCopyActiveRank(rank); + const TileXR::TileXRCcuMemoryCopyDirection p2pCcuCopyDirection = P2pCcuCopyDirectionFromEnv(); + P2pCcuCopyState p2pCcuCopy; + if (p2pCcuCopyEnabled) { + options.syncResourceCount = 1; + options.sqeArgCount = 0; + options.syncInstructionCount = 7; + p2pCcuCopy.initRet = InitP2pCcuCopyState(rank, peer, &p2pCcuCopy); + } + PrintConfig(options, rankSize); + + TileXR::TileXRCcuDirectInstallAttempt attempt; + TileXRDirectCcuPreparedTasksPtr prepared = &attempt; + TileXRDirectCcuPrepareReport installReport; + int ret = p2pCcuCopyEnabled && p2pCcuCopy.initRet != ACL_SUCCESS ? + p2pCcuCopy.initRet : + p2pCcuCopyEnabled ? + context->planner.PrepareDirectCcuMemoryCopyInstallAttempt( + context->session, + options, + reinterpret_cast(p2pCcuCopy.source.ptr), + reinterpret_cast(p2pCcuCopy.destination.ptr), + p2pCcuCopy.bytes, + static_cast(peer), + p2pCcuCopyDirection, + prepared, + &installReport) : + context->planner.PrepareDirectCcuInstallAttempt(context->session, options, prepared, &installReport); + PrintInstallReport("tilexr_ccu_direct_smoke prepare", ret, installReport); + PrintPreparedTasks(prepared, installReport.submitTaskCount); + PrintInstructionReadback(context, 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 && !p2pCcuCopyActiveRank) { + finalRet = RunInactiveP2pCcuCopyRank(rank, peer, rankSize, &p2pCcuCopy, finalRet); + } 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; + if (prepared != nullptr && installReport.submitTaskCount > 0) { + PrintMissionContext(context, prepared->submitTasks.front(), "tilexr_ccu_direct_smoke"); + } + 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; + } + DirectCcuSmokeContext context; + const int initRet = InitCommForDirectCcuSmoke(0, rankSize, rank, devices[rank], &context); + if (initRet != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke threadMode direct CCU init ret=" + << initRet + << " rank=" << rank + << " device=" << devices[rank] + << std::endl; + rankResults[rank] = 5; + return; + } + rankResults[rank] = RunPreparedSmokeForRank(&context, 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; + } + + DirectCcuSmokeContext context; + ret = InitCommForDirectCcuSmoke(commDomain, rankSize, rank, device, &context); + if (ret != TileXR::TILEXR_SUCCESS) { + std::cerr << "tilexr_ccu_direct_smoke direct CCU context init ret=" << ret << std::endl; + aclrtResetDevice(device); + aclFinalize(); + return 5; + } + + int finalRet = RunPreparedSmokeForRank(&context, 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 DirectCcuSmokeContext shutdown"); + context.backend.Shutdown(); + context.session.Shutdown(); + TraceLifecycle("after DirectCcuSmokeContext shutdown"); + 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 00000000..a0e8ba9b --- /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 00000000..142552df --- /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 00000000..1ba2d8ab --- /dev/null +++ b/tests/ccu/run_tilexr_ccu_direct_smoke.sh @@ -0,0 +1,1065 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2026 TileXR Project +# +# Multi-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=( + EID + EID_INDEX + 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 +} + +signal_wait_mode_enabled() +{ + [ "${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT:-0}" = "1" ] || + [ "${TILEXR_CCU_DIRECT_SMOKE_BARRIER:-0}" = "1" ] +} + +alltoall_mode_enabled() +{ + [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL:-0}" = "1" ] +} + +alltoall_mesh_mode_enabled() +{ + [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH:-0}" = "1" ] +} + +alltoall_long_mission_enabled() +{ + [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-0}" = "1" ] +} + +sync_xn_ping_mode_enabled() +{ + [ "${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING:-0}" = "1" ] +} + +apply_p2p_ccu_copy_defaults() +{ + if [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY:-0}" != "1" ]; then + return + fi + + export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}" + export TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK="${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK:-0}" + 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_RESOURCE_WINDOW_REGISTRATION_MODE="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE:-ra_ctx}" + 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}" + export TILEXR_CCU_DIRECT_BARRIER_MODE="${TILEXR_CCU_DIRECT_BARRIER_MODE:-sync_cke}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE="${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE:-hcomm_cap}" + export TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN="${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN:-1}" +} + +apply_signal_wait_defaults() +{ + if ! signal_wait_mode_enabled; then + return + fi + + export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}" + export TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK="${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK:-0}" + 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:-0}" + export TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-1}" + 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_RESOURCE_WINDOW_REGISTRATION_MODE="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE:-ra_ctx}" + 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_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}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE="${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE:-hcomm_cap}" +} + +apply_sync_xn_ping_defaults() +{ + if ! sync_xn_ping_mode_enabled; then + return + fi + + export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}" + export TILEXR_CCU_ALLTOALL_BYTES="${TILEXR_CCU_ALLTOALL_BYTES:-2097152}" + export TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP="${TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP:-8}" + 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:-0}" + export TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-1}" + export TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-2}" + 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_RESOURCE_WINDOW_REGISTRATION_MODE="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE:-ra_ctx}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE="${TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE:-hcomm_die}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION="${TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION:-hcomm}" + export TILEXR_CCU_PROBE_XN_START="${TILEXR_CCU_PROBE_XN_START:-1961}" + export TILEXR_CCU_PROBE_REMOTE_XN_START="${TILEXR_CCU_PROBE_REMOTE_XN_START:-2361}" + export TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START:-332}" + export TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START:-364}" + 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_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}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE="${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE:-hcomm_cap}" + export TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN="${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN:-1}" +} + +apply_alltoall_defaults() +{ + if ! alltoall_mode_enabled; then + return + fi + + export TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}" + if alltoall_mesh_mode_enabled; then + export TILEXR_CCU_ALLTOALL_BYTES="${TILEXR_CCU_ALLTOALL_BYTES:-131072}" + else + export TILEXR_CCU_ALLTOALL_BYTES="${TILEXR_CCU_ALLTOALL_BYTES:-2097152}" + fi + export TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP="${TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP:-8}" + export TILEXR_CCU_ALLTOALL_LOOP_COUNT="${TILEXR_CCU_ALLTOALL_LOOP_COUNT:-1}" + 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}" + if alltoall_mesh_mode_enabled; then + mesh_rank_size="$(parse_int "${TILEXR_CCU_RANK_SIZE:-${TILEXR_CCU_PROBE_RANK_SIZE:-2}}" 2)" + mesh_peer_count=$((mesh_rank_size - 1)) + mesh_completion_cke_count=$(((mesh_peer_count + 15) / 16)) + mesh_chunk_bytes="$(parse_int "${TILEXR_CCU_ALLTOALL_BYTES:-131072}" 131072)" + mesh_block_count=$((mesh_chunk_bytes / 32768)) + mesh_pre_sync_count=$((3 + mesh_peer_count * 3)) + mesh_copy_per_block=$((mesh_peer_count * 6 + 9 + mesh_completion_cke_count)) + if [ "${mesh_peer_count}" -gt 16 ]; then + mesh_remote_xn_count=${mesh_peer_count} + else + mesh_remote_xn_count=16 + fi + mesh_instruction_count=$((mesh_pre_sync_count + mesh_block_count * mesh_copy_per_block + mesh_peer_count * 2 + 1)) + mesh_local_cke_count=$((mesh_peer_count + 1 + mesh_completion_cke_count)) + export TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE="${TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_OFFSET_SOURCE:-hcomm_die}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION="${TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION:-hcomm}" + export TILEXR_CCU_PROBE_SQE_ARG_COUNT="${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-0}" + export TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-${mesh_peer_count}}" + export TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-${mesh_instruction_count}}" + export TILEXR_CCU_PROBE_XN_START="${TILEXR_CCU_PROBE_XN_START:-1961}" + export TILEXR_CCU_PROBE_REMOTE_XN_START="${TILEXR_CCU_PROBE_REMOTE_XN_START:-2361}" + export TILEXR_CCU_PROBE_REMOTE_XN_COUNT="${TILEXR_CCU_PROBE_REMOTE_XN_COUNT:-${mesh_remote_xn_count}}" + export TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START:-332}" + export TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT:-${mesh_local_cke_count}}" + export TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START:-364}" + export TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT:-${mesh_peer_count}}" + export TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX:-3}" + elif [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-0}" = "1" ]; then + long_mission_bytes="$(parse_int "${TILEXR_CCU_ALLTOALL_BYTES:-2097152}" 2097152)" + long_mission_block_count=$((long_mission_bytes / 32768)) + long_mission_instruction_count=$((7 + long_mission_block_count * 7)) + export TILEXR_CCU_PROBE_SQE_ARG_COUNT="${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-13}" + export TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-3}" + export TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-${long_mission_instruction_count}}" + else + export TILEXR_CCU_PROBE_SQE_ARG_COUNT="${TILEXR_CCU_PROBE_SQE_ARG_COUNT:-0}" + export TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-1}" + export TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-7}" + fi + 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_RESOURCE_WINDOW_REGISTRATION_MODE="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE:-ra_ctx}" + 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}" + export TILEXR_CCU_DIRECT_BARRIER_MODE="${TILEXR_CCU_DIRECT_BARRIER_MODE:-sync_cke}" + export TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE="${TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE:-hcomm_cap}" + export TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN="${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN:-1}" +} + +apply_p2p_ccu_copy_defaults +apply_signal_wait_defaults +apply_sync_xn_ping_defaults +apply_alltoall_defaults + +rank_size="$(parse_int "${TILEXR_CCU_RANK_SIZE:-${TILEXR_CCU_PROBE_RANK_SIZE:-2}}" 2)" +if [ "${rank_size}" -lt 1 ]; then + echo "ERROR: rank size must be positive: ${rank_size}" >&2 + exit 2 +fi +if alltoall_mesh_mode_enabled && { [ "${rank_size}" -lt 2 ] || [ "${rank_size}" -gt 64 ]; }; then + echo "ERROR: direct CCU alltoall mesh rank size must be in [2,64]: ${rank_size}" >&2 + exit 2 +fi +devices="${TILEXR_CCU_SMOKE_DEVICES:-${TILEXR_TEST_DEVICES:-0,1}}" +IFS=',' read -r -a device_list <<< "${devices}" +if [ "${#device_list[@]}" -ne "${rank_size}" ]; then + echo "ERROR: device count ${#device_list[@]} does not match rank size ${rank_size}: ${devices}" >&2 + exit 2 +fi +declare -A seen_devices=() +for device in "${device_list[@]}"; do + if [ -z "${device}" ]; then + echo "ERROR: empty device in list: ${devices}" >&2 + exit 2 + fi + if [ "${seen_devices[${device}]+set}" = "set" ]; then + echo "ERROR: duplicate device ${device} in list: ${devices}" >&2 + exit 2 + fi + seen_devices["${device}"]=1 +done + +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_RESOURCE_WINDOW_REGISTRATION_MODE \ + TILEXR_CCU_DIRECT_INSTALL_ORDER \ + TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT \ + TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK \ + TILEXR_CCU_DIRECT_SMOKE_BARRIER \ + TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING \ + TILEXR_CCU_DIRECT_SMOKE_ALLTOALL \ + TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH \ + TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION \ + TILEXR_CCU_ALLTOALL_BYTES \ + TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP \ + TILEXR_CCU_ALLTOALL_LOOP_COUNT \ + 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 + echo "dryRun TILEXR_CCU_PROBE_RANK_SIZE=${rank_size} devices=${devices}" + 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:-}" + for ((rank=0; rank&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" + +if [ "${TILEXR_CCU_SMOKE_REUSE_PROBE:-0}" != "1" ] || [ ! -x "${probe_bin}" ]; then + c++ -std=c++14 \ + -DTILEXR_CCU_TESTING=1 \ + -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}" +fi + +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}" +rm -rf "${ready_dir}" "${done_dir}" +mkdir -p "${ready_dir}" "${done_dir}" +rank_logs=() +for ((rank=0; rank&2 + exit 2 + fi + 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_RESOURCE_WINDOW_REGISTRATION_MODE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE=${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_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_DIRECT_TRACE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_TRACE=${TILEXR_CCU_DIRECT_TRACE}") +fi +if [ "${TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE=${TILEXR_CCU_DIRECT_TRACE_ENDPOINT_ROUTE}") +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_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING=${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR=${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_SINGLE_ROUTE_BIDIRECTIONAL:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_SINGLE_ROUTE_BIDIRECTIONAL=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_SINGLE_ROUTE_BIDIRECTIONAL}") +fi +if [ "${TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC=${TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC}") +fi +if [ "${TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC_WAIT:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC_WAIT=${TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC_WAIT}") +fi +if [ "${TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_ON_COPY_ROUTE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_ON_COPY_ROUTE=${TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_ON_COPY_ROUTE}") +fi +if [ "${TILEXR_CCU_ALLTOALL_BYTES:-}" != "" ]; then + common_env+=("TILEXR_CCU_ALLTOALL_BYTES=${TILEXR_CCU_ALLTOALL_BYTES}") +fi +if [ "${TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP:-}" != "" ]; then + common_env+=("TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP=${TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP}") +fi +if [ "${TILEXR_CCU_ALLTOALL_LOOP_COUNT:-}" != "" ]; then + common_env+=("TILEXR_CCU_ALLTOALL_LOOP_COUNT=${TILEXR_CCU_ALLTOALL_LOOP_COUNT}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN=${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_TRACE_LIFECYCLE:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_TRACE_LIFECYCLE=${TILEXR_CCU_DIRECT_SMOKE_TRACE_LIFECYCLE}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_RESOURCE_WINDOW:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_RESOURCE_WINDOW=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_RESOURCE_WINDOW}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK}") +fi +if [ "${TILEXR_CCU_DIRECT_SMOKE_BARRIER:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SMOKE_BARRIER=${TILEXR_CCU_DIRECT_SMOKE_BARRIER}") +fi +if [ "${TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT:-}" != "" ]; then + common_env+=("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=${TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT}") +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 +done + +build_rank_env() +{ + local rank="$1" + rank_env=() + local mapping generic rank_var rank_value + for mapping in \ + XN_START \ + REMOTE_XN_START \ + REMOTE_XN_COUNT \ + LOCAL_WAIT_CKE_START \ + LOCAL_WAIT_CKE_COUNT \ + REMOTE_NOTIFY_CKE_START \ + REMOTE_NOTIFY_CKE_COUNT; do + generic="TILEXR_CCU_PROBE_${mapping}" + rank_var="TILEXR_CCU_PROBE_RANK${rank}_${mapping}" + rank_value="${!rank_var:-${!generic:-}}" + if [ -n "${rank_value}" ]; then + rank_env+=("${generic}=${rank_value}") + fi + done + for endpoint_field in "${endpoint_fields[@]}"; do + generic="TILEXR_CCU_DIRECT_LOCAL_ENDPOINT_${endpoint_field}" + rank_var="${generic}_RANK${rank}" + rank_value="${!rank_var:-${!generic:-}}" + if [ -n "${rank_value}" ]; then + rank_env+=("${generic}=${rank_value}") + fi + done + for token_field in "${resource_window_token_fields[@]}"; do + generic="TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}" + rank_var="${generic}_RANK${rank}" + rank_value="${!rank_var:-${!generic:-}}" + if [ -n "${rank_value}" ]; then + rank_env+=("${generic}=${rank_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} syncXnPing=${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING:-0} alltoall=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL:-0} alltoallLongMission=${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-0} signalWait=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT:-0} signalRank=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK:-0} ccuBarrier=${TILEXR_CCU_DIRECT_SMOKE_BARRIER:-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 alltoall_mode_enabled; then + if [ "$(grep -c "tilexr_ccu_alltoall prepare ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU alltoall 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 alltoall 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 alltoall thread-mode submit requested but prepare did not reach submitReady=1" >&2 + exit 6 + fi + if [ "$(grep -c "tilexr_ccu_alltoall submit ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU alltoall thread-mode submit did not return success for both ranks" >&2 + exit 7 + fi + if [ "$(grep -c "tilexr_ccu_alltoall timing" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU alltoall thread-mode timing was not reported for both ranks" >&2 + exit 8 + fi + fi + if [ "$(grep -c "tilexr_ccu_alltoall result passed=1" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU alltoall thread-mode result did not pass for both ranks" >&2 + exit 8 + fi + elif signal_wait_mode_enabled; then + if [ "$(grep -c "tilexr_ccu_signal_wait prepare ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU signal/wait 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 signal/wait 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 signal/wait thread-mode submit requested but prepare did not reach submitReady=1" >&2 + exit 6 + fi + if [ "$(grep -c "tilexr_ccu_signal_wait submit ret=0" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU signal/wait thread-mode submit did not return success for both ranks" >&2 + exit 7 + fi + if [ "$(grep -c "tilexr_ccu_signal_wait timing" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU signal/wait thread-mode timing was not reported for both ranks" >&2 + exit 8 + fi + fi + if [ "$(grep -c "tilexr_ccu_signal_wait result passed=1" "${thread_log}")" -lt 2 ]; then + echo "ERROR: direct CCU signal/wait thread-mode result did not pass for both ranks" >&2 + exit 8 + fi + else + 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 + 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 + +rank_pids=() +rank_statuses=() +for ((rank=0; rank "${rank_logs[${rank}]}" 2>&1 & + rank_pids+=("$!") + if [ "${rank}" -eq 0 ] && [ "${rank_size}" -gt 1 ]; then + sleep "${TILEXR_CCU_SMOKE_RANK1_DELAY:-1}" + fi +done + +any_rank_failed=0 +for ((rank=0; rank&2 + exit 4 +fi + +if alltoall_mode_enabled; then + for log in "${rank_logs[@]}"; do + if ! grep -q "tilexr_ccu_alltoall prepare ret=0" "${log}"; then + echo "ERROR: direct CCU alltoall prepare did not return success in ${log}" >&2 + exit 5 + fi + if ! grep -q "installSucceeded=1" "${log}"; then + echo "ERROR: direct CCU alltoall prepare did not complete install attempt in ${log}" >&2 + exit 6 + fi + done +elif signal_wait_mode_enabled; then + for log in "${rank_logs[@]}"; do + if ! grep -q "tilexr_ccu_signal_wait prepare ret=0" "${log}"; then + echo "ERROR: direct CCU signal/wait prepare did not return success in ${log}" >&2 + exit 5 + fi + if ! grep -q "installSucceeded=1" "${log}"; then + echo "ERROR: direct CCU signal/wait prepare did not complete install attempt in ${log}" >&2 + exit 6 + fi + done +else + for log in "${rank_logs[@]}"; 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 +fi + +rank_skipped_p2p_ccu_copy_submit() +{ + local log="$1" + [ "${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY:-0}" = "1" ] && + grep -q "tilexr_ccu_direct_smoke p2pCcuCopy skipped" "${log}" +} + +if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]; then + for log in "${rank_logs[@]}"; 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 "${rank_logs[@]}"; do + if alltoall_mode_enabled; then + if ! grep -q "tilexr_ccu_alltoall submit ret=0" "${log}"; then + echo "ERROR: direct CCU alltoall submit did not return success in ${log}" >&2 + exit 7 + fi + if ! grep -q "tilexr_ccu_alltoall timing" "${log}"; then + echo "ERROR: direct CCU alltoall timing was not reported in ${log}" >&2 + exit 8 + fi + continue + fi + if signal_wait_mode_enabled; then + if ! grep -q "tilexr_ccu_signal_wait submit ret=0" "${log}"; then + echo "ERROR: direct CCU signal/wait submit did not return success in ${log}" >&2 + exit 7 + fi + if ! grep -q "tilexr_ccu_signal_wait timing" "${log}"; then + echo "ERROR: direct CCU signal/wait timing was not reported in ${log}" >&2 + exit 8 + fi + continue + fi + if ! grep -q "tilexr_ccu_direct_smoke submit ret=0" "${log}"; then + if rank_skipped_p2p_ccu_copy_submit "${log}"; then + continue + fi + 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 + if rank_skipped_p2p_ccu_copy_submit "${log}"; then + continue + fi + echo "ERROR: direct CCU submit timing was not reported in ${log}" >&2 + exit 8 + fi + done +fi + +if alltoall_mode_enabled && [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]; then + loop_count="$(parse_int "${TILEXR_CCU_ALLTOALL_LOOP_COUNT:-1}" 1)" + expected_results=$((rank_size * loop_count)) + if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-0}" = "1" ] && + [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH:-0}" != "1" ]; then + expected_loop_results=$((rank_size * loop_count)) + actual_loop_results="$(grep -h -c "tilexr_ccu_alltoall loopResult passed=1" "${rank_logs[@]}" | awk '{ total += $1 } END { print total + 0 }')" + echo "tilexr_ccu_direct_smoke_runner alltoallLoopCounts expectedResults=${expected_loop_results} actualResults=${actual_loop_results}" + if [ "${actual_loop_results}" -ne "${expected_loop_results}" ]; then + echo "ERROR: direct CCU alltoall loop result count mismatch expected=${expected_loop_results} actual=${actual_loop_results}" >&2 + exit 9 + fi + expected_results="${rank_size}" + fi + actual_results="$(grep -h -c "tilexr_ccu_alltoall result passed=1" "${rank_logs[@]}" | awk '{ total += $1 } END { print total + 0 }')" + echo "tilexr_ccu_direct_smoke_runner alltoallCounts expectedResults=${expected_results} actualResults=${actual_results}" + if [ "${actual_results}" -ne "${expected_results}" ]; then + echo "ERROR: direct CCU alltoall result count mismatch expected=${expected_results} actual=${actual_results}" >&2 + exit 9 + fi +elif signal_wait_mode_enabled; then + for log in "${rank_logs[@]}"; do + if ! grep -q "tilexr_ccu_signal_wait result passed=1" "${log}"; then + echo "ERROR: direct CCU signal/wait result did not pass in ${log}" >&2 + exit 9 + 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="${rank_logs[1]}" + else + wait_log="${rank_logs[0]}" + fi + wait_sync_ms="$( + awk ' + /tilexr_ccu_direct_smoke submitTiming|tilexr_ccu_signal_wait timing/ { + 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 + p2p_passed_count=0 + for log in "${rank_logs[@]}"; 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 + p2p_passed_count=$((p2p_passed_count + 1)) + continue + fi + if grep -q "tilexr_ccu_direct_smoke p2pCcuCopy skipped" "${log}"; then + continue + 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 + if [ "${p2p_passed_count}" -lt 1 ]; then + echo "ERROR: direct CCU P2P CCU-copy produced no passing receiver result" >&2 + exit 13 + fi +fi + +echo "tilexr_ccu_direct_smoke_runner success workDir=${work_dir}" diff --git a/tests/ccu/test_tilexr_ccu_alltoall_program.py b/tests/ccu/test_tilexr_ccu_alltoall_program.py new file mode 100644 index 00000000..1027808b --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_alltoall_program.py @@ -0,0 +1,804 @@ +#!/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] +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" +ALLTOALL_HEADER = COMM_DIR / "ccu" / "tilexr_ccu_alltoall_program.h" +ALLTOALL_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_alltoall_program.cpp" +MICROCODE_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_microcode.cpp" +MEMORY_SOURCE = COMM_DIR / "ccu" / "tilexr_ccu_memory_program.cpp" +COMM_CMAKE = COMM_DIR / "CMakeLists.txt" + + +PRIVATE_CCU_PRODUCER_NEEDLES = [ + "#include + #include + + using namespace TileXR; + + constexpr uint16_t kSetCkeHeader = 0x0802U; + constexpr uint16_t kClearCkeHeader = 0x0804U; + constexpr uint16_t kLoadSqeArgsToXnHeader = 0x0001U; + constexpr uint16_t kLoadImdToXnHeader = 0x0003U; + constexpr uint16_t kSyncXnHeader = 0x100dU; + + uint16_t Slot(const TileXRCcuInstr& instr, uint32_t slot) + { + const uint32_t word = slot / 4U; + const uint32_t shift = (slot % 4U) * 16U; + return static_cast((instr.words[word] >> shift) & 0xffffU); + } + + int main() + { + TileXRCcuAllToAll2RankProgramSpec spec; + spec.localSendAddr = 0x10000000ULL; + spec.localSendToken = TileXRCcuPackMemoryToken(0x12345, 0x11112222U, true); + spec.localRecvAddr = 0x18000000ULL; + spec.localRecvToken = TileXRCcuPackMemoryToken(0x12346, 0x22223333U, true); + spec.remoteSendAddr = 0x20000000ULL; + spec.remoteSendToken = TileXRCcuPackMemoryToken(0x23456, 0x33334444U, true); + spec.remoteRecvAddr = 0x28000000ULL; + spec.remoteRecvToken = TileXRCcuPackMemoryToken(0x23457, 0x44445555U, true); + spec.bytes = 2ULL * 1024ULL * 1024ULL; + spec.memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + spec.memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; + spec.localGsa = 0x101; + spec.remoteGsa = 0x102; + spec.localXn = 0x201; + spec.remoteXn = 0x202; + spec.lengthXn = 0x203; + spec.preSyncLocalAddrXn = 0x209; + spec.preSyncLocalTokenXn = 0x20a; + spec.preSyncLocalMarkerXn = 0x208; + spec.preSyncRemoteMarkerXn = 0x210; + spec.preSyncMarkerArgIndex = 0; + spec.preSyncMarkerEnabled = true; + spec.channelId = 0x12; + spec.preSyncMarkerChannelId = 0x14; + spec.preSyncChannelId = 0x13; + spec.preSyncTokenChannelId = 0x13; + spec.copyCompletionCke = 0x301; + spec.preSyncRemoteAddrXn = 0x211; + spec.preSyncRemoteTokenXn = 0x212; + spec.preSyncLocalWaitCke = 0x302; + spec.preSyncRemoteNotifyCke = 0x303; + spec.preSyncTokenLocalWaitCke = 0x302; + spec.preSyncRemoteTokenNotifyCke = 0x303; + spec.postSyncLocalWaitCke = 0x306; + spec.postSyncRemoteNotifyCke = 0x307; + spec.sourceCke = 0x308; + spec.ckeMask = 1; + + std::vector program; + TileXRCcuAllToAllProgramReport report; + int ret = TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report); + if (ret != TILEXR_SUCCESS) { + std::cerr << "builder failed: " << report.message << "\n"; + return 1; + } + const uint32_t expectedBlocks = 64; + const uint32_t expectedInstructions = 7 + expectedBlocks * 7 + 3 + 1; + if (report.blockCount != expectedBlocks || + report.copyInstructionCount != expectedBlocks * 7 || + report.preSyncInstructionCount != 7 || + report.postSyncInstructionCount != 3 || + report.finishInstructionCount != 1 || + report.totalInstructionCount != expectedInstructions || + program.size() != expectedInstructions) { + std::cerr << "unexpected report counts" + << " blocks=" << report.blockCount + << " copyInst=" << report.copyInstructionCount + << " pre=" << report.preSyncInstructionCount + << " post=" << report.postSyncInstructionCount + << " finish=" << report.finishInstructionCount + << " total=" << report.totalInstructionCount + << " size=" << program.size() << "\n"; + return 2; + } + if (report.bytesPerBlock != 32768 || report.message != "ok") { + std::cerr << "unexpected block size or message\n"; + return 3; + } + const uint16_t markerMask = 1U; + const uint16_t outputMask = 1U << TILEXR_CCU_ALLTOALL_OUTPUT_XN_ID; + const uint16_t tokenMask = 1U << TILEXR_CCU_ALLTOALL_TOKEN_XN_ID; + const uint16_t waitMask = markerMask | outputMask | tokenMask; + const uint16_t syncMask = spec.ckeMask; + const uint32_t postSyncSetIndex = expectedInstructions - 4; + const uint32_t postSyncWaitIndex = expectedInstructions - 2; + if (program.size() < 7 || + Slot(program[0], 0) != kLoadSqeArgsToXnHeader || + Slot(program[0], 1) != spec.preSyncLocalMarkerXn || + Slot(program[0], 2) != spec.preSyncMarkerArgIndex || + Slot(program[1], 0) != kSyncXnHeader || + Slot(program[1], 1) != spec.preSyncRemoteMarkerXn || + Slot(program[1], 2) != spec.preSyncLocalMarkerXn || + Slot(program[1], 4) != spec.preSyncMarkerChannelId || + Slot(program[1], 5) != spec.preSyncRemoteNotifyCke || + Slot(program[1], 6) != markerMask || + Slot(program[2], 0) != kLoadImdToXnHeader || + Slot(program[2], 1) != spec.preSyncLocalAddrXn || + Slot(program[3], 0) != kSyncXnHeader || + Slot(program[3], 1) != spec.preSyncRemoteAddrXn || + Slot(program[3], 2) != spec.preSyncLocalAddrXn || + Slot(program[3], 6) != outputMask || + Slot(program[4], 0) != kLoadImdToXnHeader || + Slot(program[4], 1) != spec.preSyncLocalTokenXn || + Slot(program[5], 0) != kSyncXnHeader || + Slot(program[5], 1) != spec.preSyncRemoteTokenXn || + Slot(program[5], 2) != spec.preSyncLocalTokenXn || + Slot(program[5], 4) != spec.preSyncTokenChannelId || + Slot(program[5], 5) != spec.preSyncRemoteTokenNotifyCke || + Slot(program[5], 6) != tokenMask || + Slot(program[6], 0) != kSetCkeHeader || + Slot(program[6], 4) != spec.preSyncLocalWaitCke || + Slot(program[6], 5) != waitMask) { + std::cerr << "PreSync should publish marker, output, and token then wait for all" + << " loadHeader=0x" << std::hex << Slot(program[0], 0) + << " loadXn=0x" << Slot(program[0], 1) + << " notifyHeader=0x" << Slot(program[1], 0) + << " remoteXn=0x" << Slot(program[1], 1) + << " localXn=0x" << Slot(program[1], 2) + << " channel=0x" << Slot(program[1], 4) + << " remoteCke=0x" << Slot(program[1], 5) + << " notifyMask=0x" << Slot(program[1], 6) + << " tokenNotifyHeader=0x" << Slot(program[5], 0) + << " tokenRemoteXn=0x" << Slot(program[5], 1) + << " tokenLocalXn=0x" << Slot(program[5], 2) + << " tokenChannel=0x" << Slot(program[5], 4) + << " tokenRemoteCke=0x" << Slot(program[5], 5) + << " tokenNotifyMask=0x" << Slot(program[5], 6) + << " waitHeader=0x" << Slot(program[6], 0) + << " waitCke=0x" << Slot(program[6], 4) + << " waitMask=0x" << Slot(program[6], 5) + << std::dec << "\n"; + return 4; + } + if (Slot(program[postSyncSetIndex], 0) != kSetCkeHeader || + Slot(program[postSyncSetIndex], 3) != syncMask || + Slot(program[postSyncWaitIndex], 0) != kClearCkeHeader || + Slot(program[postSyncWaitIndex], 4) != spec.postSyncLocalWaitCke || + Slot(program[postSyncWaitIndex], 5) != syncMask) { + std::cerr << "PostSync should use the allocated post CKE resource with the resource mask" + << " postSetMask=0x" << std::hex << Slot(program[postSyncSetIndex], 3) + << " postWaitCke=0x" << Slot(program[postSyncWaitIndex], 4) + << " postWaitMask=0x" << Slot(program[postSyncWaitIndex], 5) + << " syncMask=0x" << spec.ckeMask + << std::dec << "\n"; + return 5; + } + + spec.preSyncNotify = false; + spec.postSyncNotify = false; + spec.emitFinish = false; + ret = TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report); + if (ret != TILEXR_SUCCESS || + report.preSyncInstructionCount != 0 || + report.postSyncInstructionCount != 0 || + report.finishInstructionCount != 0 || + report.totalInstructionCount != expectedBlocks * 7 || + program.size() != expectedBlocks * 7) { + std::cerr << "copy-only diagnostic program has unexpected counts\n"; + return 6; + } + + spec.preSyncNotify = true; + spec.preSyncWait = false; + spec.sourceCke = 0; + ret = TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report); + if (ret != TILEXR_SUCCESS || + report.preSyncInstructionCount != 6 || + report.totalInstructionCount != expectedBlocks * 7 + 6 || + program.size() != expectedBlocks * 7 + 6) { + std::cerr << "notify-only PreSync program has unexpected counts\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_two_rank_program_uses_same_hccl_style_copy_region_for_both_ranks(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_alltoall_program.h" + + #include + #include + + using namespace TileXR; + + constexpr uint16_t kTransLocMemToRmtMemHeader = 0x1009U; + + uint16_t Header(const TileXRCcuInstr& instr) + { + return static_cast(instr.words[0] & 0xffffU); + } + + TileXRCcuAllToAll2RankProgramSpec ValidSpec(uint32_t localRank) + { + TileXRCcuAllToAll2RankProgramSpec spec; + spec.localRank = localRank; + spec.localSendAddr = 0x10000000ULL; + spec.localSendToken = TileXRCcuPackMemoryToken(0x12345, 0x11112222U, true); + spec.localRecvAddr = 0x18000000ULL; + spec.localRecvToken = TileXRCcuPackMemoryToken(0x12346, 0x22223333U, true); + spec.remoteSendAddr = 0x20000000ULL; + spec.remoteSendToken = TileXRCcuPackMemoryToken(0x23456, 0x33334444U, true); + spec.remoteRecvAddr = 0x28000000ULL; + spec.remoteRecvToken = TileXRCcuPackMemoryToken(0x23457, 0x44445555U, true); + spec.bytes = 2ULL * 1024ULL * 1024ULL; + spec.memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + spec.memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; + spec.localGsa = 0x101; + spec.remoteGsa = 0x102; + spec.localXn = 0x201; + spec.remoteXn = 0x202; + spec.lengthXn = 0x203; + spec.preSyncLocalMarkerXn = 0x201; + spec.preSyncRemoteMarkerXn = 0x213; + spec.preSyncMarkerArgIndex = 0; + spec.preSyncMarkerEnabled = true; + spec.channelId = 0x12; + spec.preSyncMarkerChannelId = spec.channelId; + spec.copyCompletionCke = 0x301; + spec.preSyncRemoteAddrXn = 0x211; + spec.preSyncRemoteTokenXn = 0x212; + spec.preSyncLocalWaitCke = 0x302; + spec.preSyncRemoteNotifyCke = 0x303; + spec.preSyncTokenLocalWaitCke = 0x304; + spec.preSyncRemoteTokenNotifyCke = 0x305; + spec.postSyncLocalWaitCke = 0x306; + spec.postSyncRemoteNotifyCke = 0x307; + spec.sourceCke = 0x308; + spec.ckeMask = 1; + return spec; + } + + size_t FirstCopyIndex(const std::vector& program) + { + for (size_t i = 0; i < program.size(); ++i) { + if (Header(program[i]) == kTransLocMemToRmtMemHeader) { + return i; + } + } + return program.size(); + } + + uint32_t CopyInstructionCount(const std::vector& program) + { + uint32_t count = 0; + for (const auto& instr : program) { + if (Header(instr) == kTransLocMemToRmtMemHeader) { + ++count; + } + } + return count; + } + + int main() + { + std::vector rank0; + std::vector rank1; + TileXRCcuAllToAllProgramReport report0; + TileXRCcuAllToAllProgramReport report1; + int ret0 = TileXRCcuBuildAllToAll2RankProgram(ValidSpec(0), &rank0, &report0); + int ret1 = TileXRCcuBuildAllToAll2RankProgram(ValidSpec(1), &rank1, &report1); + if (ret0 != TILEXR_SUCCESS || ret1 != TILEXR_SUCCESS) { + std::cerr << "builder failed rank0=" << report0.message + << " rank1=" << report1.message << "\n"; + return 1; + } + const uint32_t expectedInstructions = 7 + 64 * 7 + 3 + 1; + if (report0.totalInstructionCount != expectedInstructions || + report1.totalInstructionCount != expectedInstructions || + rank0.size() != expectedInstructions || + rank1.size() != expectedInstructions) { + std::cerr << "unexpected instruction count" + << " rank0=" << rank0.size() + << " rank1=" << rank1.size() + << " report0=" << report0.totalInstructionCount + << " report1=" << report1.totalInstructionCount << "\n"; + return 2; + } + if (CopyInstructionCount(rank0) != 64 || CopyInstructionCount(rank1) != 64) { + std::cerr << "each rank should issue exactly 64 local-to-remote writes\n"; + return 3; + } + const size_t rank0FirstCopy = FirstCopyIndex(rank0); + const size_t rank1FirstCopy = FirstCopyIndex(rank1); + if (rank0FirstCopy != 12 || rank1FirstCopy != 12) { + std::cerr << "copy region should start at the same instruction after the single PreSync" + << " rank0FirstCopy=" << rank0FirstCopy + << " rank1FirstCopy=" << rank1FirstCopy << "\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_builder_rejects_invalid_slice_configuration(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_alltoall_program.h" + + #include + #include + + using namespace TileXR; + + TileXRCcuAllToAll2RankProgramSpec ValidSpec() + { + TileXRCcuAllToAll2RankProgramSpec spec; + spec.localSendAddr = 0x10000000ULL; + spec.localSendToken = TileXRCcuPackMemoryToken(1, 2, true); + spec.localRecvAddr = 0x18000000ULL; + spec.localRecvToken = TileXRCcuPackMemoryToken(2, 3, true); + spec.remoteSendAddr = 0x20000000ULL; + spec.remoteSendToken = TileXRCcuPackMemoryToken(3, 4, true); + spec.remoteRecvAddr = 0x28000000ULL; + spec.remoteRecvToken = TileXRCcuPackMemoryToken(4, 5, true); + spec.bytes = 2ULL * 1024ULL * 1024ULL; + spec.memorySliceBytes = TILEXR_CCU_ALLTOALL_MEMORY_SLICE_BYTES; + spec.memSlicePerBlock = TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_BLOCK; + spec.localGsa = 1; + spec.remoteGsa = 2; + spec.localXn = 3; + spec.remoteXn = 4; + spec.lengthXn = 5; + spec.channelId = 6; + spec.copyCompletionCke = 7; + spec.preSyncRemoteAddrXn = 13; + spec.preSyncRemoteTokenXn = 14; + spec.preSyncLocalWaitCke = 8; + spec.preSyncRemoteNotifyCke = 9; + spec.preSyncTokenLocalWaitCke = 10; + spec.preSyncRemoteTokenNotifyCke = 11; + spec.postSyncLocalWaitCke = 12; + spec.postSyncRemoteNotifyCke = 15; + spec.sourceCke = 16; + spec.ckeMask = 1; + return spec; + } + + int main() + { + std::vector program; + TileXRCcuAllToAllProgramReport report; + + auto spec = ValidSpec(); + spec.memSlicePerBlock = 9; + if (TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("memSlicePerBlock") == std::string::npos) { + std::cerr << "memSlicePerBlock > 8 accepted: " << report.message << "\n"; + return 1; + } + + spec = ValidSpec(); + spec.bytes = 4097; + if (TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("4KB") == std::string::npos) { + std::cerr << "non-4KB size accepted: " << report.message << "\n"; + return 2; + } + + spec = ValidSpec(); + spec.remoteRecvToken = 0; + if (TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("token") == std::string::npos) { + std::cerr << "missing token accepted: " << report.message << "\n"; + return 3; + } + + spec = ValidSpec(); + spec.localRank = 2; + if (TileXRCcuBuildAllToAll2RankProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("localRank") == std::string::npos) { + std::cerr << "invalid localRank accepted: " << 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_builder_is_wired_and_has_no_hccl_dependency_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = ALLTOALL_HEADER.read_text(encoding="utf-8") + source = ALLTOALL_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_alltoall_program.h", cmake) + self.assertIn("ccu/tilexr_ccu_alltoall_program.cpp", cmake) + self.assertIn("TileXRCcuBuildAllToAll2RankProgram", header) + self.assertIn("TileXRCcuBuildMemoryCopyProgram", source) + self.assertIn("TileXRCcuMemoryCopyDirection::LocalToRemote", source) + self.assertIn("preSyncRemoteTokenNotifyCke", header) + self.assertIn("preSyncTokenLocalWaitCke", header) + self.assertNotIn("tokenLocalWaitCke", source) + self.assertIn("PreSyncSignalMask", source) + self.assertIn("PostSyncSignalMask", source) + self.assertIn("1U << TILEXR_CCU_ALLTOALL_OUTPUT_XN_ID", source) + self.assertIn("1U << TILEXR_CCU_ALLTOALL_TOKEN_XN_ID", source) + self.assertNotIn("1U << TILEXR_CCU_ALLTOALL_POST_SYNC_ID", source) + self.assertIn("post.clearWait = true;", source) + self.assertIn("TILEXR_CCU_ALLTOALL_SIGNAL_MASK", header) + self.assertIn("TILEXR_CCU_ALLTOALL_RANK0_SIGNAL_MASK", header) + self.assertIn("TILEXR_CCU_ALLTOALL_RANK1_SIGNAL_MASK", header) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_local_rank_does_not_split_the_long_mission_into_copy_phases(self): + header = ALLTOALL_HEADER.read_text(encoding="utf-8") + source = ALLTOALL_SOURCE.read_text(encoding="utf-8") + orchestrator = (COMM_DIR / "ccu" / "tilexr_ccu_direct_orchestrator.cpp").read_text(encoding="utf-8") + planner = (COMM_DIR / "ccu" / "tilexr_ccu_collective_planner.cpp").read_text(encoding="utf-8") + + self.assertIn("uint32_t localRank = 0", header) + self.assertNotIn("append copy only for the local rank's active phase", source) + self.assertNotIn("for (uint32_t phase = 0; phase < 2U; ++phase)", source) + self.assertIn("alltoallSpec.localRank = alltoall.localRank", orchestrator) + self.assertIn("alltoall.localRank = static_cast(rank)", planner) + + def test_four_rank_mesh_posts_all_peers_then_copies_remote_and_self_chunks(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_alltoall_program.h" + + #include + #include + #include + + using namespace TileXR; + + uint16_t Slot(const TileXRCcuInstr& instr, uint32_t slot) + { + return static_cast((instr.words[slot / 4U] >> ((slot % 4U) * 16U)) & 0xffffU); + } + + uint64_t Immediate(const TileXRCcuInstr& instr) + { + return (instr.words[0] >> 32U) | (instr.words[1] << 32U); + } + + TileXRCcuAllToAllMeshPeerSpec Peer(uint32_t localRank, uint32_t peerRank, uint16_t ordinal) + { + TileXRCcuAllToAllMeshPeerSpec peer; + peer.peerRank = peerRank; + auto& route = peer.route; + route.localRank = localRank; + route.localSendAddr = 0x10000000ULL; + route.localSendToken = TileXRCcuPackMemoryToken(1, 2, true); + route.localRecvAddr = 0x20000000ULL; + route.localRecvToken = TileXRCcuPackMemoryToken(2, 3, true); + route.remoteRecvAddr = 0x30000000ULL + static_cast(peerRank) * 0x1000000ULL; + route.remoteRecvToken = TileXRCcuPackMemoryToken(10 + peerRank, 20 + peerRank, true); + route.bytes = 2ULL * 1024ULL * 1024ULL; + route.localGsa = 0x100; + route.remoteGsa = 0x101; + route.localXn = 0x200; + route.remoteXn = 0x301; + route.lengthXn = 0x202; + route.preSyncLocalAddrXn = 0x200; + route.preSyncLocalTokenXn = 0x201; + route.preSyncRemoteAddrXn = 0x300; + route.preSyncRemoteTokenXn = 0x301; + route.preSyncMarkerEnabled = false; + route.preSyncChannelId = static_cast(0x10 + ordinal); + route.preSyncTokenChannelId = route.preSyncChannelId; + route.copyChannelId = route.preSyncChannelId; + route.postSyncChannelId = route.preSyncChannelId; + route.copyCompletionCke = 0x491; + route.preSyncLocalWaitCke = static_cast(0x401 + ordinal); + route.preSyncTokenLocalWaitCke = route.preSyncLocalWaitCke; + route.preSyncRemoteNotifyCke = static_cast(0x500 + ordinal); + route.preSyncRemoteTokenNotifyCke = route.preSyncRemoteNotifyCke; + route.postSyncLocalWaitCke = route.preSyncLocalWaitCke; + route.postSyncRemoteNotifyCke = route.preSyncRemoteNotifyCke; + route.sourceCke = 0x490; + route.ckeMask = 0x8; + return peer; + } + + int main() + { + TileXRCcuAllToAllMeshProgramSpec spec; + spec.rankSize = 4; + spec.localRank = 2; + spec.localSendAddr = 0x10000000ULL; + spec.localSendToken = TileXRCcuPackMemoryToken(1, 2, true); + spec.localRecvAddr = 0x20000000ULL; + spec.localRecvToken = TileXRCcuPackMemoryToken(2, 3, true); + spec.chunkBytes = 2ULL * 1024ULL * 1024ULL; + spec.selfSourceGsa = 0x180; + spec.selfDestinationGsa = 0x181; + spec.selfSourceXn = 0x200; + spec.selfDestinationXn = 0x201; + spec.selfLengthXn = 0x202; + spec.selfChannelId = 0; + spec.selfCompletionCke = 0x480; + spec.remoteCompletionCkes = {0x491}; + spec.peers = {Peer(2, 3, 2), Peer(2, 0, 0), Peer(2, 1, 1)}; + + std::vector program; + TileXRCcuAllToAllProgramReport report; + const int ret = TileXRCcuBuildAllToAllMeshProgram(spec, &program, &report); + if (ret != TILEXR_SUCCESS) { + std::cerr << report.message << "\n"; + return 1; + } + if (report.peerCount != 3 || report.syncResourceCount != 3 || + report.remoteBlockCount != 192 || report.selfBlockCount != 64 || + report.preSyncInstructionCount != 12 || report.copyInstructionCount != 1792 || + report.postSyncInstructionCount != 6 || report.finishInstructionCount != 1 || + report.totalInstructionCount != 1811 || program.size() != 1811) { + std::cerr << "unexpected mesh counts total=" << program.size() << "\n"; + return 2; + } + // Match HCCL: load both values, initialize source CKE, then post output/token per channel. + if (Slot(program[0], 1) != 0x200U || Slot(program[1], 1) != 0x201U || + Slot(program[2], 0) != 0x0802U || Slot(program[2], 2) != 0x490U || + Slot(program[2], 3) != 0xffffU) { + std::cerr << "unexpected HCCL-style presync prelude\n"; + return 3; + } + for (uint32_t ordinal = 0; ordinal < 3; ++ordinal) { + const uint32_t output = 3 + ordinal * 2; + const uint32_t token = output + 1; + if (Slot(program[output], 0) != 0x100dU || Slot(program[output], 6) != 0x2U || + Slot(program[token], 0) != 0x100dU || Slot(program[token], 6) != 0x4U || + Slot(program[output], 4) != Slot(program[token], 4)) { + std::cerr << "presync output/token are not paired by channel\n"; + return 4; + } + } + for (uint32_t i = 9; i < 12; ++i) { + if (Slot(program[i], 0) != 0x0802U || Slot(program[i], 5) != 0x6U) { + std::cerr << "missing presync wait mask\n"; + return 5; + } + } + // Sorted peer 0 copy: send[target=0] -> recv_peer0[source=2]. + if (Immediate(program[12]) != spec.localSendAddr || + Immediate(program[14]) != 0x30000000ULL + 2ULL * spec.chunkBytes || + Slot(program[17], 0) != 0x1009U) { + std::cerr << "unexpected first remote copy offsets\n"; + return 6; + } + const uint32_t selfStart = 30; + const uint64_t selfOffset = 2ULL * spec.chunkBytes; + if (Immediate(program[selfStart]) != spec.localSendAddr + selfOffset || + Immediate(program[selfStart + 2]) != spec.localRecvAddr + selfOffset || + Slot(program[selfStart + 5], 0) != 0x1000U || + Slot(program[selfStart + 5], 1) != 0U || + Slot(program[selfStart + 5], 5) != 0U || + Slot(program[selfStart + 7], 0) != 0x1002U || + Slot(program[selfStart + 7], 3) != 0U || + Slot(program[selfStart + 7], 5) != 0U) { + std::cerr << "unexpected CCU self copy offsets\n"; + return 7; + } + auto corrupted = program; + corrupted[3].words[1] ^= 1ULL; + if (TileXRCcuValidateAllToAllMeshProgramBindings(spec, corrupted, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("output SyncXn") == std::string::npos) { + std::cerr << "corrupted output channel accepted: " << report.message << "\n"; + return 8; + } + auto sharedRemoteIds = spec; + for (uint32_t ordinal = 1; ordinal < sharedRemoteIds.peers.size(); ++ordinal) { + sharedRemoteIds.peers[ordinal].route.remoteXn = sharedRemoteIds.peers[0].route.remoteXn; + sharedRemoteIds.peers[ordinal].route.preSyncRemoteAddrXn = + sharedRemoteIds.peers[0].route.preSyncRemoteAddrXn; + sharedRemoteIds.peers[ordinal].route.preSyncRemoteTokenXn = + sharedRemoteIds.peers[0].route.preSyncRemoteTokenXn; + sharedRemoteIds.peers[ordinal].route.preSyncRemoteNotifyCke = + sharedRemoteIds.peers[0].route.preSyncRemoteNotifyCke; + sharedRemoteIds.peers[ordinal].route.preSyncRemoteTokenNotifyCke = + sharedRemoteIds.peers[0].route.preSyncRemoteTokenNotifyCke; + sharedRemoteIds.peers[ordinal].route.postSyncRemoteNotifyCke = + sharedRemoteIds.peers[0].route.postSyncRemoteNotifyCke; + } + if (TileXRCcuBuildAllToAllMeshProgram(sharedRemoteIds, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "per-peer remote resource IDs rejected: " << report.message << "\n"; + return 7; + } + auto overlappingCke = spec; + overlappingCke.remoteCompletionCkes[0] = overlappingCke.peers[0].route.sourceCke; + for (auto& peer : overlappingCke.peers) { + peer.route.copyCompletionCke = overlappingCke.remoteCompletionCkes[0]; + } + if (TileXRCcuBuildAllToAllMeshProgram(overlappingCke, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("overlaps source CKE") == std::string::npos) { + std::cerr << "overlapping source/completion CKE accepted: " << report.message << "\n"; + return 8; + } + auto duplicate = spec; + duplicate.peers[1].route.copyChannelId = duplicate.peers[0].route.copyChannelId; + if (TileXRCcuBuildAllToAllMeshProgram(duplicate, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL || + report.message.find("duplicate") == std::string::npos) { + std::cerr << "duplicate peer resource accepted: " << report.message << "\n"; + return 8; + } + for (uint32_t localRank = 0; localRank < 4; ++localRank) { + auto rankSpec = spec; + rankSpec.localRank = localRank; + rankSpec.peers.clear(); + uint16_t ordinal = 0; + for (uint32_t peerRank = 0; peerRank < 4; ++peerRank) { + if (peerRank != localRank) { + rankSpec.peers.push_back(Peer(localRank, peerRank, ordinal++)); + } + } + if (TileXRCcuBuildAllToAllMeshProgram(rankSpec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "rank " << localRank << " rejected: " << report.message << "\n"; + return 9; + } + const uint64_t rankOffset = static_cast(localRank) * rankSpec.chunkBytes; + if (program.size() != 1811 || + Immediate(program[selfStart]) != rankSpec.localSendAddr + rankOffset || + Immediate(program[selfStart + 2]) != rankSpec.localRecvAddr + rankOffset) { + std::cerr << "rank " << localRank << " self offset mismatch\n"; + return 9; + } + } + auto spec8 = spec; + spec8.rankSize = 8; + spec8.localRank = 3; + spec8.peers.clear(); + spec8.remoteCompletionCkes = {0x491}; + uint16_t ordinal8 = 0; + for (uint32_t peerRank = 0; peerRank < spec8.rankSize; ++peerRank) { + if (peerRank != spec8.localRank) { + spec8.peers.push_back(Peer(spec8.localRank, peerRank, ordinal8++)); + } + } + if (TileXRCcuBuildAllToAllMeshProgram(spec8, &program, &report) != TILEXR_SUCCESS || + program.size() != 3367 || report.peerCount != 7 || report.syncResourceCount != 7 || + report.remoteBlockCount != 448 || report.selfBlockCount != 64) { + std::cerr << "unexpected 8-rank 2MB mesh: " << report.message + << " instructions=" << program.size() << "\n"; + return 10; + } + + auto spec2 = spec; + spec2.rankSize = 2; + spec2.localRank = 0; + spec2.peers = {Peer(0, 1, 0)}; + spec2.remoteCompletionCkes = {0x491}; + if (TileXRCcuBuildAllToAllMeshProgram(spec2, &program, &report) != TILEXR_SUCCESS || + program.size() != 1033 || report.peerCount != 1 || report.syncResourceCount != 1 || + report.remoteBlockCount != 64 || report.selfBlockCount != 64) { + std::cerr << "unexpected 2-rank 2MB full mesh: " << report.message + << " instructions=" << program.size() << "\n"; + return 11; + } + + auto spec64 = spec; + spec64.rankSize = 64; + spec64.localRank = 17; + spec64.chunkBytes = 128ULL * 1024ULL; + spec64.peers.clear(); + spec64.remoteCompletionCkes = {0x491, 0x492, 0x493, 0x494}; + uint16_t ordinal64 = 0; + for (uint32_t peerRank = 0; peerRank < spec64.rankSize; ++peerRank) { + if (peerRank == spec64.localRank) { + continue; + } + auto peer = Peer(spec64.localRank, peerRank, ordinal64); + peer.route.bytes = spec64.chunkBytes; + peer.route.copyCompletionCke = spec64.remoteCompletionCkes[ordinal64 / 16U]; + spec64.peers.push_back(peer); + ++ordinal64; + } + if (TileXRCcuBuildAllToAllMeshProgram(spec64, &program, &report) != TILEXR_SUCCESS || + program.size() != 1883 || report.peerCount != 63 || report.syncResourceCount != 63 || + report.remoteBlockCount != 252 || report.selfBlockCount != 4) { + std::cerr << "unexpected 64-rank 128KB mesh: " << report.message + << " instructions=" << program.size() << "\n"; + return 12; + } + 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_backend_boundary.py b/tests/ccu/test_tilexr_ccu_backend_boundary.py new file mode 100644 index 00000000..689af500 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_backend_boundary.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +COMM_HEADER = REPO_ROOT / "src" / "comm" / "tilexr_comm.h" +PUBLIC_API_HEADER = REPO_ROOT / "src" / "include" / "tilexr_api.h" +BACKEND_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_backend.h" +BACKEND_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_backend.cpp" +RUNTIME_SESSION_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_runtime_session.h" +RUNTIME_SESSION_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_runtime_session.cpp" +PLANNER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_collective_planner.h" +PLANNER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_collective_planner.cpp" +EXECUTOR_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_executor.h" +EXECUTOR_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_executor.cpp" + + +class TileXRCcuBackendBoundaryTest(unittest.TestCase): + def test_backend_files_exist(self): + self.assertTrue(BACKEND_HEADER.exists()) + self.assertTrue(BACKEND_SOURCE.exists()) + + def test_backend_internals_are_split(self): + for path in [ + RUNTIME_SESSION_HEADER, + RUNTIME_SESSION_SOURCE, + PLANNER_HEADER, + PLANNER_SOURCE, + EXECUTOR_HEADER, + EXECUTOR_SOURCE, + ]: + with self.subTest(path=path.name): + self.assertTrue(path.exists()) + + def test_tilexr_comm_header_owns_only_opaque_backend(self): + header = COMM_HEADER.read_text(encoding="utf-8") + self.assertIn("class TileXRCcuBackend;", header) + self.assertIn("std::unique_ptr ccuBackend_", header) + for needle in [ + "tilexr_ccu_direct_orchestrator.h", + "tilexr_ccu_direct_runtime.h", + "tilexr_ccu_lower_layer_plan_builder.h", + "TileXRCcuDirectRuntime", + "directCcuBasicInfo_", + "directCcuLowerLayerPlan_", + "directCcuVerifiedEndpointRoutes_", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + + def test_backend_header_exposes_facade_not_public_c_api(self): + header = BACKEND_HEADER.read_text(encoding="utf-8") + public_api = PUBLIC_API_HEADER.read_text(encoding="utf-8") + self.assertIn("class TileXRCcuBackend", header) + self.assertIn("struct TileXRCcuBackendOptions", header) + self.assertIn("TileXRSockExchange *exchange", header) + self.assertIn("PrepareCollective", header) + self.assertIn("SubmitCollective", header) + for needle in [ + "enum class TileXRCcuSignalWaitRole", + "struct TileXRCcuSignalWaitRequest", + "struct TileXRCcuSignalWaitPlan", + "PrepareSignalWait", + "SubmitSignalWait", + ]: + with self.subTest(internal=needle): + self.assertIn(needle, header) + for needle in [ + "TileXRDirectCcuPreparedTasksPtr", + "TileXRCommPrepareDirectCcu", + "TileXRDirectCcuSubmitPrepared", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + self.assertNotIn(needle, public_api) + for needle in [ + "TileXRCcuSignalWait", + "PrepareSignalWait", + "SubmitSignalWait", + ]: + with self.subTest(public_needle=needle): + self.assertNotIn(needle, public_api) + + def test_split_sources_own_restored_direct_ccu_runtime_glue(self): + source = BACKEND_SOURCE.read_text(encoding="utf-8") + runtime_header = RUNTIME_SESSION_HEADER.read_text(encoding="utf-8") + runtime = RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + executor = EXECUTOR_SOURCE.read_text(encoding="utf-8") + self.assertIn("#include \"ccu/tilexr_ccu_runtime_session.h\"", source) + self.assertIn("#include \"ccu/tilexr_ccu_collective_planner.h\"", source) + self.assertIn("#include \"ccu/tilexr_ccu_executor.h\"", source) + self.assertNotIn("#include \"ccu/tilexr_ccu_direct_runtime.h\"", source) + self.assertNotIn("#include \"ccu/tilexr_ccu_repository.h\"", source) + self.assertNotIn("TileXRCcuDirectRuntime", source) + + for needle in [ + "#include \"ccu/tilexr_ccu_direct_runtime.h\"", + "TileXRCcuDirectRuntime", + "DirectCcuThreadAllGather", + "g_directCcuAllGatherStates", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, runtime_header + "\n" + runtime) + + for needle in [ + "#include \"ccu/tilexr_ccu_repository.h\"", + "PrepareDirectCcuInstallAttempt", + "PrepareSignalWait", + "PrepareDirectCcuLowerLayerPlanCallback", + "TileXRCcuRunDirectInstallAttempt", + "TileXRCcuRunDirectSignalWaitInstallAttempt", + "TileXRCcuMakeRepositoryDeviceMemoryOps", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, planner) + + self.assertIn("planner_->PrepareSignalWait", source) + self.assertNotIn("return TILEXR_ERROR_NOT_SUPPORT;", source[source.index("PrepareSignalWait"):]) + + planner_header = PLANNER_HEADER.read_text(encoding="utf-8") + executor_header = EXECUTOR_HEADER.read_text(encoding="utf-8") + self.assertRegex( + planner_header, + r"(?s)#ifdef TILEXR_CCU_TESTING.*PrepareDirectCcuMemoryCopyInstallAttempt.*#endif", + ) + self.assertRegex( + planner, + r"(?s)#ifdef TILEXR_CCU_TESTING.*PrepareDirectCcuMemoryCopyInstallAttempt.*#endif", + ) + self.assertRegex( + executor_header, + r"(?s)#ifdef TILEXR_CCU_TESTING.*ReadDirectCcuInstructionsForDebug.*#endif", + ) + self.assertRegex( + executor, + r"(?s)#ifdef TILEXR_CCU_TESTING.*ReadDirectCcuInstructionsForDebug.*#endif", + ) + for fake_ready in [ + "options_ = options;\n initialized_ = true;\n return TILEXR_SUCCESS;", + "plan->ready = true;\n return TILEXR_SUCCESS;", + "return plan.ready ? TILEXR_SUCCESS", + ]: + with self.subTest(fake_ready=fake_ready): + self.assertNotIn(fake_ready, source + "\n" + runtime + "\n" + planner + "\n" + executor) + + def test_tilexr_comm_can_auto_initialize_ccu_backend_without_blocking_comm_init(self): + source = (REPO_ROOT / "src" / "comm" / "tilexr_comm.cpp").read_text(encoding="utf-8") + + self.assertIn('constexpr const char* TILEXR_ENABLE_CCU_BACKEND_ENV = "TILEXR_ENABLE_CCU_BACKEND"', source) + self.assertIn("bool ShouldEnableCcuBackend()", source) + self.assertIn("int TileXRComm::InitCcuBackendIfEnabled()", source) + self.assertIn("const int ccuRet = InitCcuBackend()", source) + self.assertIn("TileXR CCU backend init failed, direct CCU disabled", source) + self.assertIn("TileXR CCU backend initialized", source) + + process_init = source[source.index("int TileXRComm::Init()"): source.index("int TileXRComm::InitThread")] + thread_init = source[source.index("int TileXRComm::InitThread"): source.index("int TileXRComm::EnablePeerAccess")] + + for body_name, body in [("process", process_init), ("thread", thread_init)]: + with self.subTest(body=body_name): + self.assertIn("ret = InitCcuBackendIfEnabled();", body) + self.assertLess(body.index("ret = InitCcuBackendIfEnabled();"), body.index("ret = SyncCommArgs();")) + self.assertIn("if (ret != TILEXR_SUCCESS) {", body) + self.assertIn("return ret;", body) + + helper = source[ + source.index("int TileXRComm::InitCcuBackendIfEnabled()"): + source.index("TileXRCcuBackend *TileXRComm::GetCcuBackendForCollectives") + ] + self.assertIn("if (!ShouldEnableCcuBackend())", helper) + self.assertIn("return TILEXR_SUCCESS;", helper) + self.assertNotIn("return ccuRet;", helper) + + def test_p2p_ccu_copy_process_token_is_not_urma_shifted(self): + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + token_query = planner[ + planner.index("int QueryDirectCcuProcessMemoryToken"): + planner.index("int BuildDirectCcuLocalMemoryCopyEndpoint") + ] + + self.assertIn("rtUbDevQueryInfo(QUERY_PROCESS_TOKEN, &info)", token_query) + self.assertIn("const uint32_t tokenId = info.tokenId;", token_query) + self.assertIn("TileXRCcuPackMemoryToken(tokenId, info.tokenValue, true)", token_query) + self.assertNotIn("info.tokenId >>", token_query) + + def test_p2p_ccu_copy_uses_original_va_for_microcode_and_imported_segva_for_route(self): + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + endpoint_builder = planner[ + planner.index("int BuildDirectCcuLocalMemoryCopyEndpoint"): + planner.index("void TileXRCcuCollectivePlanner::Reset") + ] + prepare_copy = planner[ + planner.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuMemoryCopyInstallAttempt"): + planner.index("int TileXRCcuCollectivePlanner::RefreshDirectCcuLowerLayerPlan") + ] + + self.assertIn("session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr)", prepare_copy) + self.assertIn("session.RegisterMemoryBuffer(sourceAddr, bytes, &sourceInfo)", endpoint_builder) + self.assertIn("session.RegisterMemoryBuffer(destinationAddr, bytes, &destinationInfo)", endpoint_builder) + self.assertIn("endpoint->sourceAddr = sourceInfo.addr", endpoint_builder) + self.assertIn("endpoint->destinationAddr = destinationInfo.addr", endpoint_builder) + self.assertNotIn("endpoint->sourceAddr = sourceInfo.targetSegVa", endpoint_builder) + self.assertNotIn("endpoint->destinationAddr = destinationInfo.targetSegVa", endpoint_builder) + self.assertIn("endpoint->sourceRemoteImport.key = sourceInfo.key", endpoint_builder) + self.assertIn("endpoint->destinationRemoteImport.key = destinationInfo.key", endpoint_builder) + self.assertIn("TileXRCcuPackMemoryToken(sourceInfo.tokenId, sourceInfo.tokenValue, true)", endpoint_builder) + self.assertIn( + "TileXRCcuPackMemoryToken(destinationInfo.tokenId, destinationInfo.tokenValue, true)", + endpoint_builder, + ) + self.assertIn("remoteImportRequest = peerEndpoint.sourceRemoteImport", prepare_copy) + self.assertIn("remoteImportRequest = peerEndpoint.destinationRemoteImport", prepare_copy) + self.assertIn("session.ImportRemoteMemoryBuffer(remoteImportRequest, &importedRemoteBuffer)", prepare_copy) + self.assertIn("memoryCopy.remoteAddr = remoteImportRequest.addr", prepare_copy) + self.assertIn("SetDirectCcuRemoteRouteMemoryOverride(", prepare_copy) + self.assertIn("importedRemoteBuffer.targetSegVa", prepare_copy) + self.assertNotIn("QueryDirectCcuProcessMemoryToken(sourceAddr", endpoint_builder) + self.assertNotIn("QueryDirectCcuProcessMemoryToken(destinationAddr", endpoint_builder) + + def test_alltoall_overrides_only_copy_route_memory_not_sync_routes(self): + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + prepare_alltoall = planner[ + planner.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuAllToAll2RankInstallAttempt"): + planner.index("#endif", planner.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuAllToAll2RankInstallAttempt")) + ] + override_apply = planner[ + planner.index("void TileXRCcuCollectivePlanner::ApplyDirectCcuRemoteRouteMemoryOverride"): + planner.index("#endif", planner.index("void TileXRCcuCollectivePlanner::ApplyDirectCcuRemoteRouteMemoryOverride")) + ] + + self.assertIn("SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute(", prepare_alltoall) + self.assertIn("peerRanks.size() < routedPeerCount", planner) + self.assertIn("0U", prepare_alltoall) + self.assertIn("uint32_t routeIndex = 0", override_apply) + self.assertIn("override.syncRouteIndex != routeIndex", override_apply) + self.assertIn("++routeIndex", override_apply) + self.assertIn("override.allRoutes", override_apply) + + def test_four_rank_mesh_gathers_imports_and_maps_three_routes_per_peer(self): + header = PLANNER_HEADER.read_text(encoding="utf-8") + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("PrepareDirectCcuAllToAllMeshInstallAttempt", header) + self.assertIn("PrepareDirectCcuAllToAllMeshInstallAttempt", planner) + mesh_body = planner[ + planner.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuAllToAllMeshInstallAttempt"): + planner.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuSyncXnPingInstallAttempt") + ] + self.assertIn("rankSize < 2", mesh_body) + self.assertIn("rankSize > 64", mesh_body) + self.assertIn("rankSize - 1", mesh_body) + self.assertEqual(1, mesh_body.count("session.AllGather(")) + self.assertIn("endpoint.rank != peerRank", mesh_body) + self.assertIn("session.ImportRemoteMemoryBuffer", mesh_body) + self.assertNotIn("routeWithinPeer", mesh_body) + self.assertNotIn("SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute", mesh_body) + self.assertNotIn("peer.imported.targetSegVa", mesh_body) + self.assertIn("ClearDirectCcuRemoteRouteMemoryOverride", mesh_body) + self.assertIn("TileXRCcuRunDirectAllToAllMeshInstallAttempt", mesh_body) + + exchange = planner[ + planner.index("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke"): + planner.index("void TileXRCcuCollectivePlanner::SetDirectCcuRemoteRouteMemoryOverride") + ] + self.assertIn("routesPerPeer = syncRouteCount / routedPeerCount", exchange) + self.assertIn("peerBufferIndex = syncIndex / routesPerPeer", exchange) + self.assertIn("peerLocalResourceOffset =", exchange) + self.assertIn("peerLocalIndex * routesPerPeer + routeWithinPeer", exchange) + + self.assertIn("std::vector", header) + self.assertIn("directCcuRemoteRouteMemoryOverrides_", header) + override_apply = planner[ + planner.index("void TileXRCcuCollectivePlanner::ApplyDirectCcuRemoteRouteMemoryOverride"): + planner.index("#endif", planner.index("void TileXRCcuCollectivePlanner::ApplyDirectCcuRemoteRouteMemoryOverride")) + ] + self.assertIn("for (const auto &override : directCcuRemoteRouteMemoryOverrides_)", override_apply) + self.assertIn("override.syncRouteIndex != routeIndex", override_apply) + + +if __name__ == "__main__": + unittest.main() 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 00000000..cbecf46a --- /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] != 0xffff02210330100bULL || + 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] != 0xffff02210330100bULL || + 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] != 0xffff02210330100bULL || + 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 00000000..654efc78 --- /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, 1983, 333, 0, true, 0, true, true, true}); + lowerLayer.remoteXnBindings.push_back({1, 4, 1963, 1991, 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_task_timeout_override_is_applied_before_install_trace_and_manifest(self): + source = DIRECT_SOURCE.read_text(encoding="utf-8") + body = source[ + source.index("int RunDirectInstallAttemptImpl("): + source.index("int TileXRCcuRunDirectMemoryCopyInstallAttempt(") + ] + + first_override = body.index("ApplyTaskTimeoutOverride(options.taskTimeout, attempt)") + self.assertLess(first_override, body.index("TraceDirectInstallAttempt(*attempt)")) + self.assertLess(first_override, body.index("TileXRCcuBuildInstallManifest")) + self.assertLess(first_override, body.index("TileXRCcuInstallHardware")) + + def test_signal_wait_uses_notify_mask_for_source_cke_reserve(self): + source = DIRECT_SOURCE.read_text(encoding="utf-8") + body = source[ + source.index("int BuildDirectSignalWaitLaunchPackage("): + source.index("void FillReportFromAttempt(") + ] + + self.assertIn("spec.remoteNotifyMask = resource.remoteNotifyMask == 0 ? 1U : resource.remoteNotifyMask", body) + self.assertIn("spec.sourceCkeMask = spec.remoteNotifyMask", body) + self.assertNotIn("spec.sourceCkeMask = resource.sourceCkeMask", body) + + 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.channels.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.channels.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 * 8U), + 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 = 24; + 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.channels.num; ++i) { + plan->remoteXnBindings.push_back({ + 1, + allocation.channels.startId + i, + static_cast(allocation.localXn.startId + i), + static_cast(allocation.remoteXn.startId + i * 8U), + 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") + planner = PLANNER_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 TileXRCcuDirectAllToAll2RankSpec", header) + self.assertIn("struct TileXRCcuDirectSignalWaitSpec", 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("TileXRCcuRunDirectAllToAll2RankInstallAttempt", header) + self.assertIn("TileXRCcuRunDirectSignalWaitInstallAttempt", 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("TileXRCcuBuildAllToAll2RankProgram", source) + self.assertIn("BuildDirectAllToAll2RankLaunchPackage", source) + self.assertIn("TileXRCcuBuildSignalWaitProgram", source) + self.assertIn("BuildDirectSignalWaitLaunchPackage", source) + self.assertIn("spec.localWaitCke = resource.localWaitCke == 0 ? resource.notifyCke : resource.localWaitCke", 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("uint16_t taskTimeout = 0", header) + self.assertIn("ApplyTaskTimeoutOverride(options.taskTimeout, attempt)", 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("decoded=TransLocMemToLocMem", 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) + self.assertIn("TILEXR_CCU_TRACE_TRANS_LOC_MEM_TO_LOC_MEM_HEADER", source) + + def test_direct_alltoall_uses_three_sync_resources_and_distinct_phases(self): + header = DIRECT_HEADER.read_text(encoding="utf-8") + source = DIRECT_SOURCE.read_text(encoding="utf-8") + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + two_rank_body = source[ + source.index("int BuildDirectAllToAll2RankLaunchPackage"): + source.index("int BuildDirectAllToAllMeshLaunchPackage") + ] + + self.assertIn("TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT = 3U", source) + self.assertIn("DirectAllToAll2RankInstructionCapacity", source) + self.assertIn("DirectAllToAll2RankInstructionCapacity", planner) + self.assertIn("alltoall != nullptr ? TILEXR_CCU_DIRECT_ALLTOALL_SYNC_RESOURCE_COUNT", source) + self.assertIn("alltoall != nullptr ? TileXRCcuBarrierMode::SyncXn", source) + self.assertIn("const TileXRCcuSyncResource& copyResource = attempt->plan.syncResources[0]", source) + self.assertIn("const TileXRCcuSyncResource& preResource = attempt->plan.syncResources[1]", source) + self.assertIn("const TileXRCcuSyncResource& postResource = attempt->plan.syncResources[2]", source) + self.assertIn("SetDirectCcuRemoteRouteMemoryOverrideForSyncRoute(\n 0U,", planner) + self.assertIn( + "static_cast(peerResources.localWaitCkeStartId) +\n peerLocalWaitCkeOffset", + planner, + ) + self.assertNotIn( + "peerResources.remoteNotifyCkeStartId) +\n peerLocalWaitCkeOffset", planner + ) + self.assertIn( + "preSyncOnCopyRoute ? copyResource.localXn : preResource.localXn", source + ) + self.assertIn( + "preSyncOnCopyRoute ? copyResource.localXn : preSyncRemoteAddrXn", source + ) + self.assertIn( + "preSyncPeerLocalXn ? postResource.localXn : postResource.remoteXn", source + ) + self.assertIn("preSyncTokenChannelId = preResource.channelId", source) + self.assertIn("preSyncLocalMarkerXn = copyResource.localXn", source) + self.assertIn("preSyncRemoteMarkerXn = copyResource.remoteXn", source) + self.assertIn("preSyncMarkerChannelId = alltoallSpec.preSyncChannelId", source) + self.assertIn("preSyncMarkerArgIndex = 0", source) + self.assertIn("preSyncMarkerEnabled = true", source) + self.assertNotIn("preSyncTokenChannelId = postResource.channelId", source) + self.assertIn( + "preSyncOnCopyRoute ? attempt->allocation.remoteNotifyCke.startId : preResource.notifyCke", source + ) + self.assertIn("preSyncRemoteTokenNotifyCke = preResource.notifyCke", source) + self.assertIn("preSyncTokenLocalWaitCke =", source) + self.assertIn("preResource.localWaitCke == 0 ? preResource.notifyCke : preResource.localWaitCke", source) + self.assertIn("alltoallSpec.copyCompletionCke =", source) + self.assertIn("copyResource.localWaitCke == 0 ? copyResource.notifyCke : copyResource.localWaitCke", source) + self.assertIn("postSyncRemoteNotifyCke = postResource.notifyCke", source) + self.assertNotIn("postSyncRemoteNotifyCke = preResource.notifyCke", source) + self.assertIn("postResource.localWaitCke == 0 ? postResource.notifyCke : postResource.localWaitCke", source) + self.assertIn( + "preSyncOnCopyRoute ? copyResource.channelId : preResource.channelId", source + ) + self.assertIn("copyChannelId = copyResource.channelId", source) + self.assertIn("postSyncChannelId = postResource.channelId", source) + self.assertNotIn("postSyncChannelId = preResource.channelId", source) + self.assertIn("postSyncNotify = false", source) + self.assertIn("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC", source) + self.assertIn("alltoallSpec.preSyncNotify", source) + self.assertIn("TILEXR_CCU_DIRECT_ALLTOALL_SKIP_PRE_SYNC_WAIT", source) + self.assertIn("alltoallSpec.preSyncWait", source) + self.assertIn("TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_ON_COPY_ROUTE", source) + self.assertIn("preSyncOnCopyRoute", source) + self.assertIn("TILEXR_CCU_DIRECT_ALLTOALL_PRE_SYNC_PEER_LOCAL_XN", source) + self.assertIn( + "preSyncPeerLocalXn ? preResource.localXn : preResource.remoteXn", source + ) + self.assertIn( + "preSyncPeerLocalXn ? postResource.localXn : postResource.remoteXn", source + ) + self.assertIn("postSyncWait = false", source) + self.assertIn("emitFinish = false", source) + self.assertNotIn("postSyncNotify = true", two_rank_body) + self.assertNotIn("postSyncWait = true", two_rank_body) + self.assertNotIn("emitFinish = true", two_rank_body) + self.assertIn("LocalToRemote", source) + self.assertIn("uint32_t memSlicePerBlock", header) + + combined = header + "\n" + source + for needle in PRIVATE_CCU_PRODUCER_NEEDLES: + with self.subTest(needle=needle): + self.assertNotIn(needle, combined) + + def test_direct_four_rank_mesh_builds_one_three_channel_launch_package(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_direct_orchestrator.h" + + #include + + using namespace TileXR; + + 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) | 4095U; + basic.caps.cap1 = (63U << 16) | 31U; + basic.caps.cap2 = (63U << 16) | 63U; + basic.caps.cap3 = (127U << 16) | 31U; + basic.caps.cap4 = 15U; + + TileXRCcuDirectInstallOptions options; + options.basicInfo = &basic; + options.deviceId = 4; + options.rank = 2; + options.provider = "unit-test-direct-alltoall-mesh"; + options.missionStartId = 6; + options.instructionStartId = 475; + options.missionInstructionStartId = 489; + options.xnStartId = 1961; + options.gsaStartId = 510; + options.ckeStartId = 332; + options.channelStartId = 2; + options.offlineOnly = true; + + TileXRCcuDirectAllToAllMeshSpec mesh; + mesh.rankSize = 4; + mesh.localRank = 2; + mesh.localSendAddr = 0x10000000ULL; + mesh.localSendToken = TileXRCcuPackMemoryToken(1, 2, true); + mesh.localRecvAddr = 0x20000000ULL; + mesh.localRecvToken = TileXRCcuPackMemoryToken(2, 3, true); + mesh.chunkBytes = 2ULL * 1024ULL * 1024ULL; + for (uint32_t peerRank : {3U, 0U, 1U}) { + TileXRCcuDirectAllToAllMeshPeerSpec peer; + peer.peerRank = peerRank; + peer.remoteRecvAddr = 0x30000000ULL + peerRank * 0x1000000ULL; + peer.remoteRecvToken = TileXRCcuPackMemoryToken(10 + peerRank, 20 + peerRank, true); + mesh.peers.push_back(peer); + } + + TileXRCcuDirectInstallAttempt attempt; + TileXRCcuDirectInstallReport report; + const int ret = TileXRCcuRunDirectAllToAllMeshInstallAttempt( + options, mesh, &attempt, &report); + (void)ret; + if (!report.pipelineBuilt || attempt.plan.syncResources.size() != 3 || + attempt.plan.taskWindows.size() != 1 || attempt.package.tasks.size() != 1 || + attempt.package.program.sync.size() != 1811 || + attempt.plan.taskWindows[0].instCnt != 1811 || + attempt.plan.kernelLocalGsa.num != 2 || attempt.allocation.sourceCke.num != 2 || + attempt.allocation.remoteXn.num != 3 || + attempt.plan.syncResources[1].remoteXn - + attempt.plan.syncResources[0].remoteXn != 1 || + attempt.plan.syncResources[2].remoteXn - + attempt.plan.syncResources[1].remoteXn != 1 || + attempt.plan.barrierMode != TileXRCcuBarrierMode::SyncCke) { + std::cerr << "unexpected mesh package: " << report.message + << " resources=" << attempt.plan.syncResources.size() + << " instructions=" << attempt.package.program.sync.size() << "\n"; + return 1; + } + auto mesh8 = mesh; + mesh8.rankSize = 8; + mesh8.peers.clear(); + for (uint32_t peerRank = 0; peerRank < mesh8.rankSize; ++peerRank) { + if (peerRank == mesh8.localRank) { + continue; + } + TileXRCcuDirectAllToAllMeshPeerSpec peer; + peer.peerRank = peerRank; + peer.remoteRecvAddr = 0x40000000ULL + peerRank * 0x1000000ULL; + peer.remoteRecvToken = TileXRCcuPackMemoryToken(30 + peerRank, 40 + peerRank, true); + mesh8.peers.push_back(peer); + } + TileXRCcuDirectInstallAttempt attempt8; + TileXRCcuDirectInstallReport report8; + const int ret8 = TileXRCcuRunDirectAllToAllMeshInstallAttempt( + options, mesh8, &attempt8, &report8); + (void)ret8; + if (!report8.pipelineBuilt || attempt8.plan.syncResources.size() != 7 || + attempt8.package.program.sync.size() != 3367 || + attempt8.plan.taskWindows[0].instCnt != 3367 || + attempt8.allocation.remoteXn.num != 7 || + attempt8.plan.syncResources[6].remoteXn - + attempt8.plan.syncResources[5].remoteXn != 1 || + attempt8.allocation.sourceCke.num != 2) { + std::cerr << "unexpected 8-rank mesh package: " << report8.message + << " resources=" << attempt8.plan.syncResources.size() + << " instructions=" << attempt8.package.program.sync.size() << "\n"; + return 3; + } + auto mesh2 = mesh; + mesh2.rankSize = 2; + mesh2.localRank = 0; + mesh2.peers.clear(); + TileXRCcuDirectAllToAllMeshPeerSpec peer2; + peer2.peerRank = 1; + peer2.remoteRecvAddr = 0x50000000ULL; + peer2.remoteRecvToken = TileXRCcuPackMemoryToken(50, 60, true); + mesh2.peers.push_back(peer2); + TileXRCcuDirectInstallAttempt attempt2; + TileXRCcuDirectInstallReport report2; + const int ret2 = TileXRCcuRunDirectAllToAllMeshInstallAttempt( + options, mesh2, &attempt2, &report2); + (void)ret2; + if (!report2.pipelineBuilt || attempt2.plan.syncResources.size() != 1 || + attempt2.package.program.sync.size() != 1033 || + attempt2.plan.taskWindows[0].instCnt != 1033 || + attempt2.allocation.localXn.num != 3 || + attempt2.allocation.remoteXn.num != 3 || + attempt2.allocation.sourceCke.num != 2) { + std::cerr << "unexpected 2-rank full mesh package: " << report2.message + << " resources=" << attempt2.plan.syncResources.size() + << " instructions=" << attempt2.package.program.sync.size() << "\n"; + return 4; + } + auto invalidMesh = mesh; + invalidMesh.rankSize = 0xffffffffU; + invalidMesh.peers.clear(); + TileXRCcuDirectInstallAttempt invalidAttempt; + TileXRCcuDirectInstallReport invalidReport; + if (TileXRCcuRunDirectAllToAllMeshInstallAttempt( + options, invalidMesh, &invalidAttempt, &invalidReport) != + TILEXR_ERROR_PARA_CHECK_FAIL || invalidReport.pipelineBuilt) { + std::cerr << "unexpected invalid rank-size result: " << invalidReport.message << "\n"; + return 5; + } + basic.caps.cap0 = (7U << 24) | (11U << 16) | 1599U; + TileXRCcuDirectInstallAttempt smallAttempt; + TileXRCcuDirectInstallReport smallReport; + if (TileXRCcuRunDirectAllToAllMeshInstallAttempt( + options, mesh, &smallAttempt, &smallReport) == TILEXR_SUCCESS || + smallReport.message.find("instruction") == std::string::npos || + smallReport.message.find("requested=") == std::string::npos || + smallReport.message.find("available=") == std::string::npos) { + std::cerr << "missing mesh capacity diagnostics: " << smallReport.message << "\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_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) + + def test_direct_memory_copy_submit_tasks_keep_planned_runtime_payload(self): + source = DIRECT_SOURCE.read_text(encoding="utf-8") + memory_copy_body = source[ + source.index("int BuildDirectMemoryCopyLaunchPackage"): + source.index("void FillReportFromAttempt") + ] + + self.assertIn("TileXRCcuBuildTasks(attempt->plan, &tasks, &planReport)", memory_copy_body) + self.assertIn("attempt->package.tasks = tasks", memory_copy_body) + self.assertNotIn("NormalizeDirectMemoryCopySubmitTasks", source) + self.assertNotIn("task.argSize = 1", source) + + def test_collective_planner_has_private_alltoall_prepare_path(self): + header = PLANNER_HEADER.read_text(encoding="utf-8") + source = PLANNER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("PrepareDirectCcuAllToAll2RankInstallAttempt", header) + self.assertIn("PrepareDirectCcuAllToAll2RankInstallAttempt", source) + self.assertIn("rankSize != 2", source) + self.assertIn("alltoall.localSendAddr = localEndpoint.sourceAddr", source) + self.assertIn("alltoall.localSendToken = localEndpoint.sourceToken", source) + self.assertIn("remoteImportRequest = peerEndpoint.destinationRemoteImport", source) + self.assertIn("alltoall.remoteRecvAddr = remoteImportRequest.addr", source) + self.assertIn("alltoall.remoteRecvToken", source) + self.assertNotIn("alltoall.remoteRecvAddr = peerEndpoint.destinationAddr", source) + self.assertIn("DirectAllToAll2RankInstructionCapacity(bytes)", source) + self.assertIn("tilexr-comm-direct-ccu-alltoall", source) + self.assertIn("TileXRCcuRunDirectAllToAll2RankInstallAttempt", source) + + def test_direct_sync_xn_ping_uses_one_mission_route_and_full_4p_transport_resources(self): + header = DIRECT_HEADER.read_text(encoding="utf-8") + source = DIRECT_SOURCE.read_text(encoding="utf-8") + planner_header = PLANNER_HEADER.read_text(encoding="utf-8") + planner = PLANNER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("struct TileXRCcuDirectSyncXnPingSpec", header) + self.assertIn("uint16_t remoteNotifyMask = 0", header) + self.assertIn("uint16_t localWaitMask = 0", header) + self.assertIn("TileXRCcuRunDirectSyncXnPingInstallAttempt", header) + self.assertIn("BuildDirectSyncXnPingLaunchPackage", source) + self.assertIn("TileXRCcuEncodeSyncXn", source) + self.assertIn("defaultRemoteNotifyMask = static_cast(1U << syncXnPing.localRank)", source) + self.assertIn("TILEXR_CCU_DIRECT_SYNC_XN_PING_INSTRUCTION_COUNT = 2U", source) + self.assertIn("SyncXnPingAllocationInstructionCount(options.syncResourceCount)", source) + self.assertIn("syncXnPing != nullptr ? TileXRCcuBarrierMode::SyncXn", source) + ping_body = source[ + source.index("int BuildDirectSyncXnPingLaunchPackage"): + source.index("void FillReportFromAttempt") + ] + self.assertNotIn("TileXRCcuEncodeSyncCke", ping_body) + self.assertNotIn("TileXRCcuEncodeSetCke", ping_body) + self.assertIn("PrepareDirectCcuSyncXnPingInstallAttempt", planner_header) + self.assertIn("PrepareDirectCcuSyncXnPingInstallAttempt", planner) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_NOTIFY_MASK", planner) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_WAIT_MASK", planner) + self.assertIn("RegisterCcuResourceRmaBuffer", planner) + self.assertIn("next.syncResourceCount = static_cast(rankSize - 1)", planner) + self.assertIn("attempt->plan.syncResources.empty()", source) + self.assertIn("syncXnPing != nullptr ? options.syncResourceCount", 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 00000000..0e5a8b06 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_direct_smoke_probe.py @@ -0,0 +1,932 @@ +#!/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", + "-DTILEXR_CCU_TESTING=1", + "-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("TileXRCcuRepositoryMemoryAllocMode::RtHbm", 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("DirectCcuSmokeContext", source) + self.assertIn("TileXRCcuRuntimeSession", source) + self.assertIn("TileXRSockExchange", 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("PrepareDirectCcuInstallAttempt", source) + self.assertIn("PrepareDirectCcuMemoryCopyInstallAttempt", source) + self.assertIn("ReadDirectCcuInstructionsForDebug", 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("PrepareDirectCcuMemoryCopyInstallAttempt", source) + self.assertIn("TileXRCcuMemoryCopyDirection::RemoteToLocal", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION", source) + self.assertIn("P2pCcuCopyDirectionFromEnv", source) + self.assertIn("TileXRCcuMemoryCopyDirection::LocalToRemote", source) + self.assertIn('"LocalToRemote"', source) + self.assertIn('"1"', source) + self.assertIn("ACL_MEMCPY_HOST_TO_DEVICE", source) + self.assertIn("ACL_MEMCPY_DEVICE_TO_HOST", source) + self.assertIn("p2pCcuCopy", source) + + def test_prepare_options_read_submit_timeout(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepare_options = source[ + source.index("TileXRDirectCcuPrepareOptions MakePrepareOptions"): + source.index("const char* SignalWaitRoleName") + ] + + self.assertIn( + 'options.taskTimeout = static_cast(EnvInt("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", 0));', + prepare_options, + ) + + def test_local_to_remote_p2p_ccu_copy_inactive_rank_checks_destination(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepared_body = source[ + source.index("int RunPreparedSmokeForRank"): + source.index("int RunThreadModeSmoke") + ] + + self.assertIn("ShouldCheckInactiveP2pCcuCopyRank", source) + self.assertIn("RunInactiveP2pCcuCopyRank", source) + self.assertIn('"local_to_remote"', source) + self.assertIn("CheckP2pCcuCopyState", source) + inactive_branch = prepared_body[ + prepared_body.index("!p2pCcuCopyActiveRank"): + prepared_body.index("} else if (submitRequested) {", prepared_body.index("!p2pCcuCopyActiveRank")) + ] + self.assertIn("RunInactiveP2pCcuCopyRank", inactive_branch) + self.assertIn("CheckP2pCcuCopyState", source[source.index("RunInactiveP2pCcuCopyRank"):]) + self.assertNotIn("TileXRDirectCcuSubmitPrepared", inactive_branch) + + def test_p2p_ccu_copy_submit_is_limited_to_active_rank(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_P2P_CCU_COPY_ACTIVE_RANK", source) + self.assertIn("IsP2pCcuCopyActiveRank", source) + self.assertIn("p2pCcuCopyActiveRank", prepared_body) + self.assertIn("!p2pCcuCopyActiveRank", prepared_body) + self.assertIn("PrintP2pCcuCopySkipped", source) + self.assertIn("inactive p2p CCU-copy rank", source) + self.assertIn("WaitForInactiveP2pCcuCopyRank", source) + inactive_branch = prepared_body[ + prepared_body.index("!p2pCcuCopyActiveRank"): + prepared_body.index("} else if (submitRequested) {", prepared_body.index("!p2pCcuCopyActiveRank")) + ] + inactive_helper = source[ + source.index("int RunInactiveP2pCcuCopyRank"): + source.index("int RunPreparedSmokeForRank") + ] + self.assertIn("RunInactiveP2pCcuCopyRank", inactive_branch) + self.assertIn("PrintP2pCcuCopySkipped", inactive_helper) + self.assertIn("WaitForInactiveP2pCcuCopyRank", inactive_helper) + self.assertNotIn("TileXRDirectCcuSubmitPrepared", inactive_branch) + + 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("PrepareDirectCcuInstallAttempt", prepared_body) + self.assertIn("TileXRDirectCcuSubmitPrepared", prepared_body) + self.assertNotIn("TileXRCommInitRankWithDomain", thread_mode_body) + self.assertNotIn("TILEXR_COMM_ID", thread_mode_body) + + def test_probe_can_run_signal_wait_through_internal_backend(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepared_body = source[ + source.index("int RunPreparedSmokeForRank"): + source.index("int RunThreadModeSmoke") + ] + + self.assertIn("ccu/tilexr_ccu_backend.h", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_BARRIER", source) + self.assertIn("TileXRCcuBackend backend", source) + self.assertIn("context->backend.Init", source) + self.assertIn("RunSignalWaitSmokeForRank", source) + self.assertIn("TileXRCcuSignalWaitRequest", source) + self.assertIn("PrepareSignalWait", source) + self.assertIn("SubmitSignalWait", source) + self.assertIn("tilexr_ccu_signal_wait prepare", source) + self.assertIn("tilexr_ccu_signal_wait submit", source) + self.assertIn("tilexr_ccu_signal_wait timing", source) + self.assertIn("tilexr_ccu_signal_wait result passed=1", source) + self.assertIn("if (SignalWaitSmokeEnabled() || BarrierSmokeEnabled())", prepared_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("TileXRSockExchange", init_helper_body) + self.assertIn("TileXRCcuBackendOptions", init_helper_body) + self.assertIn("context->session.Init(options)", 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)") + shutdown_pos = main_source.index("context.session.Shutdown()", 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, shutdown_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()") + shutdown_pos = main_source.index("context.session.Shutdown()", final_ret_pos) + + self.assertLess(final_ret_pos, fast_exit_pos) + self.assertLess(fast_exit_pos, shutdown_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.repositoryInstallOptions.window", + "options.repositoryInstallOptions.dataLenMode", + "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("TileXR::TileXRCcuInstallOrder InstallOrderFromEnv()"): + source.index("uint32_t DefaultSyncInstructionCount") + ] + + self.assertIn("TileXRCcuInstallOrder::InstallLowerLayerFirst", install_order_body) + self.assertIn("repository_first", install_order_body) + self.assertIn("TileXRCcuInstallOrder::RepositoryFirst", install_order_body) + self.assertLess( + install_order_body.index("TileXRCcuInstallOrder::InstallLowerLayerFirst"), + install_order_body.index("TileXRCcuInstallOrder::RepositoryFirst"), + ) + + def test_probe_wires_barrier_mode_env_into_prepare_options(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + prepare_options_body = source[ + source.index("TileXRDirectCcuPrepareOptions MakePrepareOptions"): + source.index("const char* SignalWaitRoleName") + ] + + self.assertIn("TileXR::TileXRCcuBarrierMode BarrierModeFromEnv()", source) + self.assertIn("options.barrierMode = BarrierModeFromEnv();", prepare_options_body) + self.assertIn('return TileXR::TileXRCcuBarrierMode::LocalCkePostOnly;', source) + self.assertIn('return TileXR::TileXRCcuBarrierMode::SyncCkePostOnly;', source) + self.assertIn('return TileXR::TileXRCcuBarrierMode::SyncXnLoadPostOnly;', source) + + 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_alltoall_submit_uses_bounded_stream_synchronize(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + alltoall_body = source[ + source.index("int RunAllToAllCopyPhase"): + source.index("int RunSignalWaitSmokeForRank") + ] + + self.assertIn("aclrtSynchronizeStreamWithTimeout", alltoall_body) + self.assertIn("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", alltoall_body) + self.assertIn("PrintMissionContext(context, attempt.submitTasks.front(), \"tilexr_ccu_alltoall\")", alltoall_body) + self.assertNotIn("const int syncRet = aclrtSynchronizeStream(stream);", alltoall_body) + + def test_alltoall_inactive_rank_still_prepares_before_host_phase_wait(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + alltoall_body = source[ + source.index("int RunAllToAllCopyPhase"): + source.index("int RunAllToAllSmokeForRank") + ] + + prepare_index = alltoall_body.index("PrepareDirectCcuMemoryCopyInstallAttempt") + inactive_index = alltoall_body.index("if (!active)") + self.assertLess(prepare_index, inactive_index) + self.assertIn("const bool phaseReady =", alltoall_body) + self.assertIn("WaitForCollectiveSubmitReadiness(rank, rankSize, installReport.submitReady, phase)", alltoall_body) + self.assertIn("WaitForCollectiveSubmitDone(rank, rankSize, finalRet, phase)", alltoall_body) + self.assertIn("RankPhaseFileStem", source) + + 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_alltoall_smoke_mode_is_opt_in_and_validates_peer_pattern(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn('kAllToAllEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL"', source) + self.assertIn('kAllToAllLongMissionEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION"', source) + self.assertIn("kAllToAllSingleRouteBidirectionalEnv", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_SINGLE_ROUTE_BIDIRECTIONAL", source) + self.assertIn('kAllToAllBytesEnv = "TILEXR_CCU_ALLTOALL_BYTES"', source) + self.assertIn('kAllToAllMemSlicePerLoopEnv = "TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP"', source) + self.assertIn("state->bytes == 16U * 1024U * 1024U", source) + self.assertIn("struct AllToAllState", source) + self.assertIn("AllToAllSmokeEnabled", source) + self.assertIn("AllToAllLongMissionEnabled", source) + self.assertIn("AllToAllSingleRouteBidirectionalEnabled", source) + self.assertIn("TileXRCcuMemoryCopyDirection::LocalToRemote", source) + self.assertIn("singleRouteBidirectional", source) + self.assertIn("InitAllToAllState", source) + self.assertIn("RunAllToAllCopyPhase", source) + self.assertIn("RunAllToAllLongMissionSmokeForRank", source) + self.assertIn("RunAllToAllSmokeForRank", source) + self.assertIn("PrepareDirectCcuMemoryCopyInstallAttempt", source) + self.assertIn("PrepareDirectCcuAllToAll2RankInstallAttempt", source) + self.assertIn("TileXRCcuMemoryCopyDirection::RemoteToLocal", source) + self.assertIn("tilexr_ccu_alltoall config", source) + self.assertIn("tilexr_ccu_alltoall result passed=1", source) + self.assertIn("BuildP2pCcuCopyPattern(peer", source) + + def test_alltoall_long_mission_reuses_prepare_with_loop_specific_state(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + body = source[ + source.index("int RunAllToAllLongMissionSmokeForRank"): + source.index("int RunAllToAllSmokeForRank") + ] + + self.assertIn('kAllToAllLoopCountEnv = "TILEXR_CCU_ALLTOALL_LOOP_COUNT"', source) + self.assertIn("AllToAllLoopCountFromEnv", source) + loop_count_body = source[ + source.index("int AllToAllLoopCountFromEnv"): + source.index("uint64_t BuildAllToAllLoopMarker") + ] + self.assertIn("std::strtol", loop_count_body) + self.assertIn("parsed < 1 || parsed > 1024", loop_count_body) + self.assertIn("BuildAllToAllLoopMarker", source) + self.assertNotIn("BuildAllToAllLoopPattern", source) + self.assertNotIn("ResetAllToAllStateForLoop", source) + self.assertIn("ReadAndValidatePeerLoopMarker", source) + self.assertIn("for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex)", body) + self.assertIn("attempt.submitTasks.front().args[0] =", body) + self.assertIn("WaitForCollectiveSubmitReadiness(", body) + self.assertIn("loopIndex);", body) + self.assertIn("WaitForCollectiveSubmitDone(rank, rankSize, finalRet, loopIndex)", body) + self.assertIn("adapter.ReadXnRange", source) + self.assertIn("peerLoopMarker", source) + loop_body = body[body.index("for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex)"):] + self.assertNotIn( + "CheckAllToAllState(&alltoall)", + loop_body[:loop_body.index("lastLoopIndex = loopIndex")], + ) + self.assertIn("CheckAllToAllState(&alltoall)", body) + self.assertIn("dataCheckDeferred=1", body) + self.assertIn("attempt.plan.syncResources[0].remoteXn", body) + self.assertNotIn("attempt.plan.syncResources[0].localXn,", body) + self.assertIn("loopIndex=", body) + self.assertLess( + body.index("PrepareDirectCcuAllToAll2RankInstallAttempt"), + body.index("for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex)"), + ) + + def test_four_rank_mesh_reuses_one_prepare_and_validates_full_matrix_each_loop(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn( + 'kAllToAllMeshEnv = "TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_MESH"', source + ) + self.assertIn("AllToAllMeshSmokeEnabled", source) + self.assertIn("InitAllToAllMeshState", source) + self.assertIn("ResetAllToAllMeshStateForLoop", source) + self.assertIn("BuildAllToAllMeshByte", source) + mesh_body = source[ + source.index("int RunAllToAllMeshLongMissionSmokeForRank"): + source.index("int RunAllToAllLongMissionSmokeForRank") + ] + self.assertNotIn("const uint32_t routeIndex = peerOrdinal * 3U", mesh_body) + self.assertNotIn("markerResource.remoteXn", mesh_body) + self.assertIn("RunAllToAllMeshLongMissionSmokeForRank", source) + mesh_dispatch = source[source.index("int RunAllToAllSmokeForRank"):] + self.assertLess( + mesh_dispatch.index("AllToAllMeshSmokeEnabled()"), + mesh_dispatch.index("rankSize != 2"), + ) + body = source[ + source.index("int RunAllToAllMeshLongMissionSmokeForRank"): + source.index("int RunAllToAllLongMissionSmokeForRank") + ] + loop = "for (int loopIndex = 0; loopIndex < loopCount; ++loopIndex)" + self.assertIn("rankSize < 2", body) + self.assertIn("rankSize > 64", body) + self.assertIn("rankSize - 1", body) + self.assertIn("PrepareDirectCcuAllToAllMeshInstallAttempt", body) + self.assertIn("aclrtCreateStream", body) + self.assertIn(loop, body) + self.assertLess(body.index("PrepareDirectCcuAllToAllMeshInstallAttempt"), body.index(loop)) + self.assertLess(body.index("aclrtCreateStream"), body.index(loop)) + self.assertNotIn("attempt.submitTasks.front().args[0] = localLoopMarker", body) + self.assertIn("WaitForCollectiveSubmitReadiness", body) + self.assertIn("WaitForCollectiveSubmitDone", body) + self.assertNotIn("peerOrdinal * 3U", body) + self.assertNotIn("ReadAndValidatePeerLoopMarker", body) + self.assertIn("CheckAllToAllState(&alltoall)", body) + self.assertIn("PrintCcuResourceState", body) + self.assertIn('" resourceCount=" << (rankSize - 1)', body) + + pattern = source[ + source.index("uint8_t BuildAllToAllMeshByte"): + source.index("int InitAllToAllMeshState") + ] + for field in ["sourceRank", "targetRank", "loopIndex", "chunkOffset"]: + with self.subTest(field=field): + self.assertIn(field, pattern) + self.assertIn("static_cast(rankSize) * state->chunkBytes", source) + self.assertIn("sourceRank=", source) + self.assertIn("chunkOffset=", source) + self.assertIn("globalOffset=", source) + + def test_sync_xn_ping_smoke_mode_is_opt_in_and_uses_bounded_sync(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn('kSyncXnPingEnv = "TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING"', source) + self.assertIn('kSyncXnPingPeerXorEnv = "TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR"', source) + self.assertIn("SyncXnPingSmokeEnabled", source) + self.assertIn("RunSyncXnPingSmokeForRank", source) + self.assertIn("PrepareDirectCcuSyncXnPingInstallAttempt", source) + sync_ping_body = source[ + source.index("int RunSyncXnPingSmokeForRank"): + source.index("int RunSignalWaitSmokeForRank") + ] + self.assertIn("AllToAllState routeState", sync_ping_body) + self.assertIn("InitAllToAllState(rank, peer, &routeState)", sync_ping_body) + self.assertIn("const int peer = rank ^ peerXor", sync_ping_body) + self.assertIn("peerXor < 1 || peerXor >= rankSize", sync_ping_body) + self.assertIn("options.syncInstructionCount = 2", sync_ping_body) + planner = (REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_collective_planner.cpp").read_text( + encoding="utf-8" + ) + self.assertIn("selectedDiagnosticPeer", planner) + self.assertIn("override.syncRouteIndex == 0U", planner) + self.assertIn("peerRanks.push_back(selectedDiagnosticPeer)", planner) + self.assertIn("selectedDiagnosticPeer >= 0 ?", planner) + runtime = (REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_direct_runtime.cpp").read_text( + encoding="utf-8" + ) + peer_route_body = runtime[ + runtime.index("int TileXRCcuDirectRuntime::PreparePeerEndpointRoutes("): + runtime.index("int TileXRCcuDirectRuntime::QueryTpHandleForPeer(", + runtime.index("int TileXRCcuDirectRuntime::PreparePeerEndpointRoutes(")) + ] + self.assertIn("offer.qpKey = state.qpInfo.key", peer_route_body) + self.assertIn("importInfo.in.key = peerOffer.qpKey", peer_route_body) + self.assertNotIn("std::copy(offer.eid.begin(), offer.eid.end(), offer.qpKey.value)", runtime) + self.assertIn( + "offer.eid = state.resourceWindow.eid", + runtime, + ) + self.assertNotIn( + "std::copy(peerOffer.eid.begin(), peerOffer.eid.end(), importInfo.in.key.value)", + runtime, + ) + self.assertIn("tilexr_ccu_sync_xn_ping prepare", source) + self.assertIn("tilexr_ccu_sync_xn_ping submit", source) + self.assertIn("tilexr_ccu_sync_xn_ping timing", source) + self.assertIn("aclrtSynchronizeStreamWithTimeout", source) + self.assertIn('PrintCcuResourceState(\n context,', sync_ping_body) + self.assertIn('"tilexr_ccu_sync_xn_ping",\n 1U', sync_ping_body) + + def test_alltoall_timeout_prints_xn_and_cke_readback(self): + source = PROBE_SOURCE.read_text(encoding="utf-8") + + self.assertIn("PrintCcuResourceState", source) + self.assertIn("adapter.ReadXnRange", source) + self.assertIn("adapter.ReadCkeRange", source) + self.assertIn("localXnStartId", source) + self.assertIn("remoteXnStartId", source) + self.assertIn("localWaitCkeStartId", source) + self.assertIn("remoteNotifyCkeStartId", source) + self.assertIn("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT", source) + self.assertIn("tilexr_ccu_sync_xn_ping result passed=1", source) + + def test_smoke_runner_forwards_alltoall_env(self): + runner = (REPO_ROOT / "tests" / "ccu" / "run_tilexr_ccu_direct_smoke.sh").read_text(encoding="utf-8") + + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL", runner) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION", runner) + self.assertIn("TILEXR_CCU_ALLTOALL_BYTES", runner) + self.assertIn("TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP", runner) + self.assertIn("TILEXR_CCU_ALLTOALL_LOOP_COUNT", runner) + self.assertIn("tilexr_ccu_direct_smoke_runner alltoallLoopCounts", runner) + self.assertIn('if [ "${TILEXR_CCU_DIRECT_SMOKE_ALLTOALL_LONG_MISSION:-0}" = "1" ]; then', runner) + self.assertIn("long_mission_instruction_count=$((7 + long_mission_block_count * 7))", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-451", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-452", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-458", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-454", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-455", runner) + self.assertNotIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-476", runner) + self.assertIn("TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-7", runner) + + 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 00000000..e5c83c3c --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_direct_smoke_runner.py @@ -0,0 +1,1586 @@ +#!/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 run_fake_mesh_runner( + self, devices="4,5,6,7", rank_size="4", loop_count="10", submit=True + ): + temp_dir = tempfile.TemporaryDirectory() + temp_path = Path(temp_dir.name) + fake_bin = temp_path / "bin" + fake_bin.mkdir() + fake_cxx = fake_bin / "c++" + fake_cxx.write_text( + "#!/usr/bin/env bash\n" + "out=''\n" + "while [ $# -gt 0 ]; do\n" + " if [ \"$1\" = -o ]; then out=$2; shift 2; else shift; fi\n" + "done\n" + "cat > \"$out\" <<'PROBE'\n" + "#!/usr/bin/env bash\n" + "rank=${TILEXR_CCU_PROBE_RANK}\n" + "rank_size=${TILEXR_CCU_PROBE_RANK_SIZE}\n" + "loops=${TILEXR_CCU_ALLTOALL_LOOP_COUNT}\n" + "echo \"tilexr_ccu_alltoall prepare ret=0 installSucceeded=1 submitReady=1\"\n" + "if [ \"${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}\" = 1 ]; then\n" + "for ((loop=0; loop "${thread_log}" 2>&1', source) + self.assertIn("ccu_thread.log", source) + self.assertIn('ccu_rank${rank}.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[@]}" "${rank_env[@]}"', 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('rank${rank}Status=', source) + self.assertIn('rank${rank}Log=', 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="${rank}"', + ]: + with self.subTest(needle=needle): + self.assertLess(gate, source.index(needle)) + + def test_runner_p2p_mode_applies_direct_ccu_resource_defaults(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn("apply_p2p_ccu_copy_defaults", source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}"', source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK="${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK:-0}"', source) + self.assertIn('TILEXR_CCU_PROBE_GSA_START="${TILEXR_CCU_PROBE_GSA_START:-510}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START="${TILEXR_CCU_PROBE_RANK0_REMOTE_XN_START:-2361}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START="${TILEXR_CCU_PROBE_RANK1_REMOTE_XN_START:-2361}"', source) + self.assertIn('TILEXR_CCU_PROBE_REMOTE_XN_COUNT="${TILEXR_CCU_PROBE_REMOTE_XN_COUNT:-8}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_RANK0_LOCAL_WAIT_CKE_START:-332}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_RANK1_LOCAL_WAIT_CKE_START:-332}"', source) + self.assertIn('TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_COUNT:-8}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_RANK0_REMOTE_NOTIFY_CKE_START:-364}"', source) + self.assertIn('TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_RANK1_REMOTE_NOTIFY_CKE_START:-364}"', source) + self.assertIn('TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_COUNT:-8}"', source) + self.assertIn('TILEXR_CCU_PROBE_CHANNEL_START="${TILEXR_CCU_PROBE_CHANNEL_START:-2}"', source) + self.assertIn('TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE="${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE:-ra_ctx}"', source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN="${TILEXR_CCU_DIRECT_SMOKE_FAST_EXIT_AFTER_RUN:-1}"', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE=${TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE}")', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_ACTIVE_RANK}")', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION=${TILEXR_CCU_DIRECT_SMOKE_P2P_CCU_COPY_DIRECTION}")', source) + self.assertIn("p2p_passed_count=0", source) + self.assertIn('grep -q "tilexr_ccu_direct_smoke p2pCcuCopy skipped"', source) + self.assertIn("direct CCU P2P CCU-copy produced no passing receiver result", source) + + def test_runner_signal_wait_mode_uses_internal_backend_defaults_and_asserts_result(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn("apply_signal_wait_defaults", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK", source) + self.assertIn("TILEXR_CCU_DIRECT_SMOKE_BARRIER", source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}"', source) + self.assertIn('TILEXR_CCU_PROBE_MISSION_START="${TILEXR_CCU_PROBE_MISSION_START:-6}"', source) + self.assertIn('TILEXR_CCU_PROBE_INSTRUCTION_START="${TILEXR_CCU_PROBE_INSTRUCTION_START:-475}"', source) + self.assertIn('TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START="${TILEXR_CCU_PROBE_MISSION_INSTRUCTION_START:-489}"', source) + self.assertIn('TILEXR_CCU_PROBE_CHANNEL_START="${TILEXR_CCU_PROBE_CHANNEL_START:-2}"', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_WAIT}")', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK=${TILEXR_CCU_DIRECT_SMOKE_SIGNAL_RANK}")', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_BARRIER=${TILEXR_CCU_DIRECT_SMOKE_BARRIER}")', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT=${TILEXR_CCU_DIRECT_SUBMIT_TIMEOUT}")', source) + self.assertIn('grep -q "tilexr_ccu_signal_wait prepare ret=0"', source) + self.assertIn('grep -q "tilexr_ccu_signal_wait submit ret=0"', source) + self.assertIn('grep -q "tilexr_ccu_signal_wait result passed=1"', source) + + def test_runner_sync_xn_ping_mode_applies_direct_ccu_resource_defaults(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn("sync_xn_ping_mode_enabled", source) + self.assertIn("apply_sync_xn_ping_defaults", source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING:-0', source) + self.assertIn('common_env+=("TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR=${TILEXR_CCU_DIRECT_SMOKE_SYNC_XN_PING_PEER_XOR}")', source) + self.assertIn('TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT="${TILEXR_CCU_DIRECT_SMOKE_DIRECT_CCU_ONLY_INIT:-1}"', source) + self.assertIn('TILEXR_CCU_ALLTOALL_BYTES="${TILEXR_CCU_ALLTOALL_BYTES:-2097152}"', source) + self.assertIn('TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP="${TILEXR_CCU_ALLTOALL_MEM_SLICE_PER_LOOP:-8}"', source) + self.assertIn('TILEXR_CCU_ALLTOALL_LOOP_COUNT="${TILEXR_CCU_ALLTOALL_LOOP_COUNT:-1}"', source) + self.assertIn('common_env+=("TILEXR_CCU_ALLTOALL_LOOP_COUNT=${TILEXR_CCU_ALLTOALL_LOOP_COUNT}")', source) + self.assertIn('TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT="${TILEXR_CCU_PROBE_SYNC_RESOURCE_COUNT:-1}"', source) + self.assertIn('TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT="${TILEXR_CCU_PROBE_SYNC_INSTRUCTION_COUNT:-2}"', source) + self.assertIn('TILEXR_CCU_PROBE_XN_START="${TILEXR_CCU_PROBE_XN_START:-1961}"', source) + self.assertIn('TILEXR_CCU_PROBE_REMOTE_XN_START="${TILEXR_CCU_PROBE_REMOTE_XN_START:-2361}"', source) + self.assertIn('TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START="${TILEXR_CCU_PROBE_LOCAL_WAIT_CKE_START:-332}"', source) + self.assertIn('TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START="${TILEXR_CCU_PROBE_REMOTE_NOTIFY_CKE_START:-364}"', source) + self.assertLess(source.index("apply_sync_xn_ping_defaults"), source.index("apply_alltoall_defaults")) + + def test_runner_allows_inactive_p2p_rank_to_skip_submit(self): + source = RUNNER.read_text(encoding="utf-8") + submit_check = source[ + source.index('if [ "${TILEXR_CCU_DIRECT_SMOKE_SUBMIT:-0}" = "1" ]', source.index("any_rank_failed=0")): + source.index('if [ "${TILEXR_CCU_DIRECT_SMOKE_EXPECT_BARRIER_WAIT:-0}" = "1" ]') + ] + + self.assertIn("rank_skipped_p2p_ccu_copy_submit", source) + self.assertIn('grep -q "tilexr_ccu_direct_smoke p2pCcuCopy skipped"', source) + self.assertIn("rank_skipped_p2p_ccu_copy_submit", submit_check) + self.assertIn("continue", submit_check) + self.assertIn("direct CCU submit did not return success", submit_check) + + 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_passes_rank_specific_resource_window_eid_index(self): + source = RUNNER.read_text(encoding="utf-8") + + self.assertIn("EID_INDEX", source[source.index("resource_window_token_fields=("):]) + self.assertIn('rank_var="${generic}_RANK${rank}"', source) + self.assertIn('echo "dryRun rank${rank} TILEXR_CCU_DIRECT_RESOURCE_WINDOW_${token_field}=${rank_token_value}"', source) + + 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 00000000..33cf6f95 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_driver_adapter.py @@ -0,0 +1,1073 @@ +#!/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_reads_xn_and_cke_ranges_from_driver_slots(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + if (in.data.dataInfo.udieIdx != 1 || in.offsetStartIdx != (in.op == 203 ? 2362 : 332) || + in.data.dataInfo.dataArraySize != 3 || in.data.dataInfo.dataLen != 24) { + return -1; + } + for (uint32_t i = 0; i < 3; ++i) { + const uint64_t value = (static_cast(in.op) << 32U) | i; + std::memcpy(&out->data.dataInfo.dataArray[i], &value, sizeof(value)); + } + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + return 1; + } + uint64_t xn[3] {}; + uint64_t cke[3] {}; + if (adapter.ReadXnRange(1, 2362, xn, 3, &report) != TILEXR_SUCCESS || + adapter.ReadCkeRange(1, 332, cke, 3, &report) != TILEXR_SUCCESS) { + std::cerr << report.message << "\n"; + return 2; + } + if (state.calls != 2 || xn[0] != (203ULL << 32U) || xn[2] != ((203ULL << 32U) | 2U) || + cke[0] != (204ULL << 32U) || cke[2] != ((204ULL << 32U) | 2U)) { + std::cerr << "resource readback 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_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_TASKKILL", header) + self.assertIn("TILEXR_CCU_U_OP_CLEAN_TASKKILL_STATE", 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("SetTaskKill", header) + self.assertIn("CleanTaskKillState", header) + self.assertIn("TILEXR_CCU_U_OP_SET_TASKKILL, &out, report", source) + self.assertIn("TILEXR_CCU_U_OP_CLEAN_TASKKILL_STATE, &out, report", source) + 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 0)", install) + self.assertIn("std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE)", install) + self.assertIn("offset += batch", install) + self.assertIn("inputOffset += batch", install) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_install_provider.py b/tests/ccu/test_tilexr_ccu_install_provider.py new file mode 100644 index 00000000..5e62452e --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_install_provider.py @@ -0,0 +1,3334 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +import os +import platform +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +INSTALL_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_install_provider.h" +INSTALL_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_install_provider.cpp" +PROVIDER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_provider.cpp" +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" +SPECS_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.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, 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 00000000..dda0ace7 --- /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 00000000..cba54f76 --- /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 + #include + + using namespace TileXR; + + int main() + { + const char* rootPath = std::getenv("TILEXR_TEST_CCU_ROOT_INFO"); + std::vector routes; + std::string message; + const int ret = TileXRCcuResolvePeerEidRoutes( + rootPath == nullptr ? "" : rootPath, + 0, + {1, 2, 3}, + &routes, + &message); + if (ret != TILEXR_SUCCESS || routes.size() != 3) { + std::cerr << "resolve failed ret=" << ret << " message=" << message << "\n"; + return 1; + } + if (routes[0].localPort != "0/8" || routes[0].localEid[5] != 0x08 || + routes[1].localPort != "0/0" || routes[1].localEid[5] != 0x00 || + routes[2].localPort != "0/7" || routes[2].localEid[5] != 0x07) { + std::cerr << "peer-specific EID mapping mismatch\n"; + return 2; + } + if (routes[0].tpType != TILEXR_CCU_HCCP_TP_TYPE_CTP || + routes[1].tpType != TILEXR_CCU_HCCP_TP_TYPE_RTP) { + std::cerr << "peer-specific TP protocol mismatch\n"; + return 3; + } + return 0; + } + ''') + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + topo_path = temp_path / "topology.json" + root_path = temp_path / "rootinfo.json" + topo_path.write_text(json.dumps({ + "edge_list": [ + {"local_a": 0, "local_a_ports": ["0/8"], "local_b": 1, "local_b_ports": ["0/7"], + "protocols": ["UB_CTP", "UB_MEM"]}, + {"local_a": 0, "local_a_ports": ["0/0"], "local_b": 2, "local_b_ports": ["0/0"]}, + {"local_a": 0, "local_a_ports": ["0/7"], "local_b": 3, "local_b_ports": ["0/7"]}, + ] + }), encoding="utf-8") + root_path.write_text(json.dumps({ + "topo_file_path": str(topo_path), + "rank_list": [ + {"device_id": device, "local_id": device, "level_list": [{"rank_addr_list": addresses}]} + for device, addresses in [ + (0, [ + {"addr": "000000000000030000100000df160100", "ports": ["0/0"]}, + {"addr": "000000000008030000100000df160900", "ports": ["0/8"]}, + {"addr": "000000000007030000100000df160800", "ports": ["0/7"]}, + ]), + (1, []), + (2, []), + (3, []), + ] + ] + }), encoding="utf-8") + env = os.environ.copy() + env["TILEXR_TEST_CCU_ROOT_INFO"] = str(root_path) + result = self.compile_and_run(code, env=env, extra_sources=[TOPOLOGY_SOURCE]) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_builds_lower_layer_install_plan_from_tilexr_owned_specs(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() + { + 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 != 128 || + 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_hcomm_pfe_window_keeps_base_and_maps_sparse_verified_jetty_context(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + + using namespace TileXR; + + int main() + { + setenv("TILEXR_CCU_DIRECT_LOWER_LAYER_PFE_PARTITION", "hcomm", 1); + TileXRCcuBasicInfo basic; + basic.dieId = 0; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {0, 2, 1}; + allocation.localXn = {0, 1961, 1}; + allocation.remoteXn = {0, 2361, 1}; + allocation.notifyCke = {0, 332, 1}; + + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0xf98000000000ULL; + remote.memoryTokenId = 0x1100; + remote.remoteEid[0] = 1; + remote.tpn = 0x51; + remote.doorbellVa = 0x3fffff85080ULL; + remote.doorbellTokenId = 0x1103; + remote.sqDepth = 8; + remote.startJettyId = 1026; + remote.endpointRouteVerified = true; + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, {remote}, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << report.message << "\n"; + return 1; + } + if (snapshot.startJettyId != 1024 || snapshot.pfeJettyCount != 128 || + snapshot.routes[0].wqeBasicBlockStartId != 64) { + std::cerr << "PFE window was narrowed\n"; + return 2; + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot( + snapshot, &plan, &report) != TILEXR_SUCCESS) { + std::cerr << report.message << "\n"; + return 3; + } + if (plan.pfes.size() != 1 || plan.jettys.size() != 1 || + plan.jettys[0].startJettyCtxId != 2 || plan.jettys[0].ctxs.size() != 1) { + std::cerr << "sparse jetty context mapping 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_uses_local_pfe_id_from_ra_eid_func_id(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, 0x120, 1}; + allocation.remoteXn = {1, 0x240, 1}; + allocation.notifyCke = {1, 0x330, 1}; + + std::vector remoteCcuBuffers; + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + remote.localPfeId = 7; + remote.localPfeIdValid = true; + 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.pfeId != 7 || snapshot.pfeOffset != 23) { + std::cerr << "local PFE id 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_prefers_ra_eid_func_id_over_channel_id_in_source(self): + source = BUILDER_SOURCE.read_text(encoding="utf-8") + build_body = source[ + source.index("int TileXRCcuBuildLowerLayerTransportTemplate"): + source.index("int TileXRCcuOverlayVerifiedEndpointRoutes") + ] + + self.assertIn("remoteCcuBuffers.front().localPfeId", build_body) + self.assertIn("allocation.channels.startId", build_body) + self.assertLess( + build_body.index("remoteCcuBuffers.front().localPfeId"), + build_body.index("SelectLowerLayerPfeOffset")) + + 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 = 3; + 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.remoteXnId = allocation.remoteXn.startId; + remote0.remoteNotifyCke = 0x360; + remoteCcuBuffers.push_back(remote0); + TileXRCcuRemoteCcuBufferInfo remote1 = remote0; + remote1.remoteCcuVa = specInfo.xnBaseAddr + (allocation.remoteXn.startId + 8U) * 8ULL; + remote1.memoryTokenId = 0x23457; + remote1.memoryTokenValue = 0x5679; + remote1.remoteXnId = static_cast(allocation.remoteXn.startId + 8U); + 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 + 8U || + 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_overlay_verified_endpoint_route_reuses_shared_jetty_for_multi_route_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); + } + + uint16_t DecodeChannelStartJettyId(const TileXRCcuChannelCtxDataV1& ctx) + { + const uint16_t word18 = Read16(ctx.raw, 18); + const uint16_t word20 = Read16(ctx.raw, 20); + return static_cast(((word18 >> 12U) & 0xfU) | ((word20 & 0xfffU) << 4U)); + } + + int main() + { + TileXRCcuBasicInfo basic; + basic.dieId = 1; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {1, 2, 3}; + allocation.localXn = {1, 0x1a0, 3}; + allocation.remoteXn = {1, 0x2a0, 3}; + allocation.notifyCke = {1, 0x360, 3}; + allocation.localWaitCke = {1, 0x220, 3}; + allocation.remoteNotifyCke = {1, 0x360, 3}; + + TileXRCcuRemoteCcuBufferInfo remote; + remote.remoteCcuVa = 0x0000009234000000ULL; + remote.memoryTokenId = 0x23456; + remote.memoryTokenValue = 0x5678; + remote.remoteXnId = 0x2a0; + remote.remoteNotifyCke = 0x360; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + remote.remoteEid[i] = static_cast(0x40 + i); + } + remote.tpn = 0x010203; + remote.doorbellVa = 0x1111222233334444ULL; + remote.doorbellTokenId = 0x12345; + remote.doorbellTokenValue = 0; + remote.sqDepth = 8; + remote.startJettyId = 0x400; + remote.endpointRouteVerified = true; + + std::vector remoteCcuBuffers {remote, remote, remote}; + for (uint32_t i = 0; i < remoteCcuBuffers.size(); ++i) { + remoteCcuBuffers[i].remoteCcuVa += i * 0x1000ULL; + remoteCcuBuffers[i].memoryTokenValue += i; + remoteCcuBuffers[i].remoteXnId = static_cast(0x2a0 + i); + remoteCcuBuffers[i].remoteNotifyCke = static_cast(0x360 + i); + } + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, remoteCcuBuffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template build failed: " << report.message << "\n"; + return 1; + } + + TileXRCcuLowerLayerTransportRoute verified = snapshot.routes[0]; + std::vector verifiedRoutes {verified}; + if (TileXRCcuOverlayVerifiedEndpointRoutes(verifiedRoutes, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "overlay failed: " << report.message << "\n"; + return 2; + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "install plan build failed: " << report.message + << " pfeJettyCount=" << snapshot.pfeJettyCount + << " routeCount=" << snapshot.routes.size() << "\n"; + return 3; + } + if (plan.jettys.empty() || plan.jettys[0].ctxs.size() != 1 || plan.pfes.empty()) { + std::cerr << "install plan shape mismatch\n"; + return 4; + } + if (plan.channels.size() != 3 || + plan.remoteXnBindings.size() != 3 || + snapshot.pfeJettyCount < snapshot.routes.size()) { + std::cerr << "multi-route lower-layer plan did not preserve all routes\n"; + return 5; + } + if (DecodeChannelStartJettyId(plan.channels[0].ctx) != 0x400 || + DecodeChannelStartJettyId(plan.channels[1].ctx) != 0x400 || + DecodeChannelStartJettyId(plan.channels[2].ctx) != 0x400) { + std::cerr << "multi-route channels did not reuse the verified endpoint jetty\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_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_shared_peer_jetty_routes_reuse_one_wqe_window_per_peer(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_lower_layer_plan_builder.h" + + #include + #include + #include + + using namespace TileXR; + + uint16_t DecodeWqeStart(const TileXRCcuLocalJettyCtxData& ctx) + { + const uint16_t word22 = static_cast(ctx.raw[22]) | + static_cast(ctx.raw[23] << 8U); + const uint16_t word24 = static_cast(ctx.raw[24]) | + static_cast(ctx.raw[25] << 8U); + return static_cast(((word22 >> 12U) & 0xfU) | ((word24 & 0xffU) << 4U)); + } + + uint16_t DecodeChannelJetty(const TileXRCcuChannelCtxDataV1& ctx) + { + const uint16_t word18 = static_cast(ctx.raw[18]) | + static_cast(ctx.raw[19] << 8U); + const uint16_t word20 = static_cast(ctx.raw[20]) | + static_cast(ctx.raw[21] << 8U); + return static_cast(((word18 >> 12U) & 0xfU) | ((word20 & 0xfffU) << 4U)); + } + + int main() + { + setenv("TILEXR_CCU_DIRECT_LOWER_LAYER_WQE_MODE", "hcomm_cap", 1); + TileXRCcuBasicInfo basic; + basic.dieId = 0; + basic.msId = 0x45; + basic.msidToken.tokenId = 0x1234; + basic.msidToken.valid = true; + + TileXRCcuResourceAllocation allocation; + allocation.channels = {0, 2, 9}; + allocation.localXn = {0, 1961, 9}; + allocation.remoteXn = {0, 2361, 9}; + allocation.notifyCke = {0, 332, 9}; + allocation.localWaitCke = {0, 332, 9}; + allocation.remoteNotifyCke = {0, 364, 9}; + + std::vector buffers(9); + for (uint32_t route = 0; route < buffers.size(); ++route) { + const uint32_t peerOrdinal = route / 3U; + auto& buffer = buffers[route]; + buffer.remoteCcuVa = 0x90000000ULL + route * 0x1000ULL; + buffer.memoryTokenId = 0x2000U + route; + buffer.memoryTokenValue = 0x3000U + route; + buffer.remoteXnId = static_cast(2361U + route); + buffer.remoteNotifyCke = static_cast(364U + route); + buffer.peerRank = peerOrdinal + 1U; + for (uint32_t byte = 0; byte < buffer.remoteEid.size(); ++byte) { + buffer.remoteEid[byte] = static_cast(0x20U + peerOrdinal * 0x10U + byte); + } + buffer.tpn = 0x50U + peerOrdinal; + buffer.doorbellVa = 0x10000000ULL + peerOrdinal * 0x10000ULL; + buffer.doorbellTokenId = 0x4000U + peerOrdinal; + buffer.sqDepth = 8; + buffer.localDoorbellVa = 0x20000000ULL + peerOrdinal * 0x10000ULL; + buffer.localDoorbellTokenId = 0x5000U + peerOrdinal; + buffer.localSqDepth = 8; + buffer.startJettyId = static_cast(1024U + peerOrdinal); + buffer.endpointRouteVerified = true; + } + + TileXRCcuLowerLayerTransportSnapshot snapshot; + TileXRCcuLowerLayerPlanBuilderReport report; + if (TileXRCcuBuildLowerLayerTransportTemplate( + basic, allocation, buffers, &snapshot, &report) != TILEXR_SUCCESS) { + std::cerr << "template failed: " << report.message << "\n"; + return 1; + } + for (uint32_t route = 0; route < snapshot.routes.size(); ++route) { + const uint16_t expected = static_cast((route / 3U) * 32U); + if (snapshot.routes[route].wqeBasicBlockStartId != expected) { + std::cerr << "route WQE mismatch route=" << route << " observed=" + << snapshot.routes[route].wqeBasicBlockStartId << "\n"; + return 2; + } + } + + TileXRCcuLowerLayerInstallPlan plan; + if (TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot(snapshot, &plan, &report) != + TILEXR_SUCCESS) { + std::cerr << "plan failed: " << report.message << "\n"; + return 3; + } + if (plan.jettys.size() != 1U || plan.jettys[0].ctxs.size() != 3U || + plan.channels.size() != 9U) { + std::cerr << "unexpected plan shape\n"; + return 4; + } + for (uint32_t peer = 0; peer < 3U; ++peer) { + if (DecodeWqeStart(plan.jettys[0].ctxs[peer]) != peer * 32U) { + std::cerr << "jetty context WQE mismatch peer=" << peer << "\n"; + return 5; + } + for (uint32_t route = 0; route < 3U; ++route) { + if (DecodeChannelJetty(plan.channels[peer * 3U + route].ctx) != 1024U + peer) { + std::cerr << "channel jetty mismatch peer=" << peer << " route=" << route << "\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_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("remoteCcuBuffers.size() != allocation.channels.num", 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", planner_source) + self.assertIn("TileXRCcuBuildLowerLayerInstallPlanFromTransportSnapshot", planner_source) + self.assertIn("session.RegisterCcuResourceRmaBuffer(basicInfo->resourceAddr)", planner_source) + self.assertIn("session.ExportRemoteCcuRmaBuffers", planner_source) + self.assertIn("ccuDirectRuntime_->ExportLowerLayerTransportSnapshot", runtime_session_source) + self.assertIn("RefreshDirectCcuLowerLayerPlan(session);", planner_source) + self.assertIn("direct CCU lower-layer template is not configured", planner_source) + self.assertIn("direct CCU lower-layer install plan cached", planner_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::InitCcuBackend") + ] + 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 + "\n" + backend_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") + backend_header = CCU_BACKEND_HEADER.read_text(encoding="utf-8") + backend_source = CCU_BACKEND_SOURCE.read_text(encoding="utf-8") + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + planner_source = CCU_PLANNER_SOURCE.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) + + for leaked in [ + "ConfigureDirectCcuLowerLayerTemplateFromAllocation(", + "PrepareDirectCcuLowerLayerTemplateFromAllocation", + "ExchangeDirectCcuRemoteNotifyCke", + "DirectCcuAllGatherCallback", + "DirectCcuThreadAllGather", + "directCcuVerifiedEndpointRoutes_", + "directCcuLocalVerifiedEndpointRoute_", + ]: + with self.subTest(leaked=leaked): + self.assertNotIn(leaked, comm_header) + self.assertNotIn(leaked, backend_header) + + self.assertIn("const std::vector &remoteCcuBuffers", planner_source) + self.assertIn("ccuDirectRuntime_->RegisterCcuResourceRmaBuffer", runtime_session_source) + self.assertIn("ccuDirectRuntime_->ExportLocalCcuRmaBuffer", runtime_session_source) + self.assertIn("ccuDirectRuntime_->ExportRemoteCcuRmaBuffers", runtime_session_source) + self.assertIn("runtimeOptions.allGather = &TileXRCcuRuntimeSession::DirectCcuAllGatherCallback", runtime_session_source) + self.assertIn("runtimeOptions.allGatherUserData = this", runtime_session_source) + self.assertIn("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke", planner_source) + self.assertIn("int TileXRCcuRuntimeSession::DirectCcuThreadAllGather", runtime_session_source) + self.assertIn("session.AllGather(&local, sizeof(local), all.data())", planner_source) + self.assertIn("session->DirectCcuThreadAllGather(sendBuf, sendBytes, recvBuf)", runtime_session_source) + self.assertIn("TileXRComm::InitCcuBackend", comm_source) + self.assertIn("return ccuBackend_->Init(options);", comm_source) + exchange_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback") + ] + self.assertNotIn("SelectDirectCcuRemoteBindingOverride", exchange_body) + self.assertIn("peerLocalWaitCkeOffset", exchange_body) + self.assertIn("peerResources.localWaitCkeCount", exchange_body) + self.assertIn("peerResources.remoteNotifyCkeCount", exchange_body) + self.assertNotIn("allocation.remoteNotifyCke.startId,\n routeIndex", exchange_body) + self.assertIn("allocation.localXn.startId", planner_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", planner_source) + self.assertIn("remoteNotifyCke", planner_source) + self.assertIn("templateSnapshot.msidToken.tokenId = localCcuResourceWindow.tokenId", planner_source) + self.assertIn("templateSnapshot.msidToken.tokenValue = localCcuResourceWindow.tokenValue", planner_source) + self.assertIn("templateSnapshot.msidToken.valid = true", planner_source) + self.assertIn("directCcuVerifiedEndpointRoutes_", planner_source) + self.assertIn("TileXRCcuCollectivePlanner::ConfigureDirectCcuVerifiedEndpointRoutes", planner_source) + self.assertIn("directCcuLocalVerifiedEndpointRoute_", planner_source) + self.assertIn("TileXRCcuCollectivePlanner::ConfigureDirectCcuLocalVerifiedEndpointRoute", planner_source) + self.assertIn("ccuDirectRuntime_->ConfigureLocalVerifiedEndpointRoute", runtime_session_source) + self.assertIn("ccuDirectRuntime_->RefreshLocalVerifiedEndpointRoute", runtime_session_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(planner_source.count("TileXRCcuOverlayVerifiedEndpointRoutes("), 3) + init_runtime_body = runtime_session_source[ + runtime_session_source.index("int TileXRCcuRuntimeSession::Init("): + runtime_session_source.index("void TileXRCcuRuntimeSession::ResetDirectCcuBasicInfo") + ] + 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 = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerTemplateFromAllocation"): + planner_source.index("int TileXRCcuCollectivePlanner::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("session.ConfigureLocalVerifiedEndpointRoute"), + prepare_from_allocation_body.index("session.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_bound_remote_xn_operand(self): + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + exchange_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback") + ] + compact_body = " ".join(exchange_body.split()) + + self.assertIn( + "peerLocalXnId = static_cast(static_cast(peerResources.localXnStartId) + peerLocalXnOffset)", + compact_body) + self.assertIn("selectedRemoteXnOffset >= peerResources.remoteXnCount", compact_body) + self.assertIn("peerLocalIndex * routesPerPeer + routeWithinPeer", compact_body) + self.assertIn("peerResources.remoteXnStartId", compact_body) + self.assertNotIn("SelectDirectCcuRemoteBindingOverride", 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.assertIn("(*remoteCcuBuffers)[routeIndex].remoteXnId = channelBoundRemoteXnId", 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_lower_layer_clears_the_complete_allocated_remote_xn_range(self): + source = BUILDER_SOURCE.read_text(encoding="utf-8") + + self.assertIn( + "remoteCcuBuffers.size() != allocation.channels.num", + source) + self.assertNotIn( + "remoteCcuBuffers.size() != allocation.remoteXn.num", + source) + self.assertIn("result.remoteXnStartId = allocation.remoteXn.startId", source) + self.assertIn("result.remoteXnCount = allocation.remoteXn.num", source) + self.assertIn("snapshot.remoteXnStartId", source) + self.assertIn("snapshot.remoteXnCount", source) + + def test_remote_notify_cke_targets_peer_local_wait_cke(self): + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + exchange_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback") + ] + compact_body = " ".join(exchange_body.split()) + + self.assertIn("peerLocalWaitCkeOffset", exchange_body) + self.assertIn("peerLocalWaitCkeOffset >= peerResources.localWaitCkeCount", compact_body) + self.assertIn("peerLocalWaitCkeOffset >= peerResources.remoteNotifyCkeCount", compact_body) + self.assertIn( + "remoteNotifyCke = static_cast(static_cast(peerResources.localWaitCkeStartId) + peerLocalWaitCkeOffset)", + compact_body) + self.assertIn("remoteNotifyCke) >= peerResources.localWaitCkeStartId", compact_body) + self.assertIn("peerResources.localWaitCkeStartId) + peerResources.localWaitCkeCount", 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): + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + exchange_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::ExchangeDirectCcuRemoteNotifyCke"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback") + ] + 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.channels.num", compact_body) + self.assertIn("allocation.remoteXn.num < routedPeerCount", compact_body) + self.assertIn("allocation.localWaitCke.num < syncRouteCount", compact_body) + self.assertIn("allocation.remoteNotifyCke.num < syncRouteCount", compact_body) + self.assertNotIn("allocation.remoteXn.num != static_cast(rankSize - 1)", compact_body) + self.assertIn("peerCcuBuffersByRank", compact_body) + self.assertIn("peerCcuBuffer.peerRank", compact_body) + self.assertIn("invalid direct CCU peer buffer rank mapping", compact_body) + self.assertIn("incomplete direct CCU peer buffer rank mapping", compact_body) + self.assertIn("remoteCcuBuffers->assign(syncRouteCount, TileXRCcuRemoteCcuBufferInfo{})", compact_body) + self.assertIn("for (uint32_t syncIndex = 0; syncIndex < syncRouteCount; ++syncIndex)", compact_body) + self.assertIn("const size_t peerBufferIndex = syncIndex / routesPerPeer", compact_body) + self.assertIn( + "(*remoteCcuBuffers)[routeIndex] = *peerCcuBuffersByRank[static_cast(peer)]", + compact_body) + self.assertIn("peerLocalIndex * routesPerPeer + routeWithinPeer", compact_body) + self.assertIn( + "SelectDirectCcuChannelBoundRemoteXnOffset( peerLocalIndex, routeWithinPeer)", + compact_body) + self.assertIn("peerResources.remoteXnStartId", 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_peer_endpoints_keep_per_peer_resource_and_jetty_tokens(self): + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn("SelectEndpointRouteJettyCtxId(state->eidInfo.funcId, peerOrdinal", source) + self.assertIn( + "TILEXR_CCU_DIRECT_LOOP_JETTY_ID + jettyCtxId", + source) + self.assertIn("SelectEndpointRouteSqVa(localResourceWindow_, jettyCtxId)", source) + self.assertIn("state->resourceWindow.tokenId = localResourceWindow_.tokenId", source) + self.assertIn("state->resourceWindow.tokenValue = localResourceWindow_.tokenValue", source) + self.assertNotIn("mr.in.ub.tokenValue = state->resourceWindow.tokenValue", source) + self.assertIn("offer.resourceTokenId = state.resourceWindow.tokenId", source) + self.assertIn("offer.resourceTokenValue = state.resourceWindow.tokenValue", source) + self.assertIn("offer.jettyTokenValue = state.jettyTokenValue", source) + self.assertIn("importInfo.in.ub.tokenValue = peerOffer.jettyTokenValue", source) + self.assertIn("state.route.memoryTokenValue = peerOffer.resourceTokenValue", source) + + 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_runtime_source_supports_selecting_ra_ctx_resource_window_eid(self): + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn("TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX", source) + self.assertIn("BuildRaCtxResourceWindowEidCandidates", source) + self.assertIn("SelectRankedEnv(TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_ENV", source) + self.assertIn("SelectDirectCcuCleanupDieId()", source) + self.assertIn("it->dieId == dieId", source) + self.assertIn("TILEXR_CCU_UBOE_DEV_FLAG_RIGHT_SHIFT = 19U", source) + self.assertIn("QueryTpHandleForPeer(ctxHandle, candidateEid, candidateEid", source) + self.assertIn("loopEidCandidate", source) + self.assertIn("TraceRaCtxEidInfos", source) + self.assertIn("ctxAttr.ub.eidIndex = candidate.eidIndex", source) + self.assertEqual(2, source.count( + "qpAttr.ub.errTimeout = TILEXR_CCU_DIRECT_ENDPOINT_ERR_TIMEOUT")) + + def test_direct_ccu_runtime_can_select_ra_ctx_resource_window_eid_by_env(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 = 2; + return 0; + } + + int FakeRaGetDevEidInfoList(TileXRCcuRaInfo, TileXRCcuHccpDevEidInfo* list, uint32_t* num) + { + if (list == nullptr || num == nullptr || *num != 2) { + return -1; + } + list[0].eidIndex = 3; + list[1].eidIndex = 9; + for (uint32_t i = 0; i < TILEXR_CCU_EID_BYTES; ++i) { + list[0].eid.raw[i] = static_cast(0xa0 + i); + list[1].eid.raw[i] = static_cast(0xc0 + i); + } + return 0; + } + + int FakeRaCtxInit(TileXRCcuHccpCtxInitCfg*, TileXRCcuHccpCtxInitAttr* attr, void** ctx) + { + if (attr == nullptr || attr->ub.eidIndex != 9 || attr->ub.eid.raw[0] != 0xc0) { + return -1; + } + *ctx = reinterpret_cast(0x1000); + return 0; + } + + int FakeRaCtxDeinit(void*) { return 0; } + int FakeRaCtxTokenIdAlloc(void*, TileXRCcuHccpTokenId* token, void** tokenHandle) + { + token->tokenId = 0x12345600U; + *tokenHandle = reinterpret_cast(0x2000); + return 0; + } + int FakeRaCtxTokenIdFree(void*, void*) { return 0; } + int FakeRaGetSecRandom(TileXRCcuRaInfo*, uint32_t* value) + { + *value = 0xabcdef01U; + return 0; + } + int FakeRaCtxLmemRegister(void*, TileXRCcuHccpMrRegInfo* mr, void** handle) + { + if (mr == nullptr || mr->in.mem.addr != kAlignedResourceAddr || + mr->in.mem.size != kAlignedResourceBytes) { + return -1; + } + mr->out.ub.tokenId = 0x12345600U; + mr->out.ub.targetSegHandle = 0x4455667788ULL; + *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 = 1; + runtime.cachedBasicInfoValid_ = true; + runtime.cachedBasicInfo_.resourceAddr = kResourceAddr; + 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 || + local.eidIndex != 9 || local.eid[0] != 0xc0) { + std::cerr << "wrong eid selection index=" << local.eidIndex + << " first=" << static_cast(local.eid[0]) << "\n"; + return 2; + } + runtime.Shutdown(); + return 0; + } + ''' + ) + env = os.environ.copy() + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_REGISTRATION_MODE"] = "ra_ctx" + env["TILEXR_CCU_DIRECT_RESOURCE_WINDOW_EID_INDEX_RANK1"] = "9" + + 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] != 0xbf || + buffers[0].remoteEid[15] != 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] != 0x7f || + buffers[0].remoteEid[15] != 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] != 0x8f || + buffers[0].remoteEid[15] != 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] != 0xaf || + buffers[0].remoteEid[15] != 0xa0 || + 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") + backend_header = CCU_BACKEND_HEADER.read_text(encoding="utf-8") + backend_source = CCU_BACKEND_SOURCE.read_text(encoding="utf-8") + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + + for leaked in [ + "RefreshDirectCcuBasicInfo", + "HasDirectCcuBasicInfo", + "GetDirectCcuBasicInfoStatus", + "GetDirectCcuBasicInfo", + "GetDirectCcuBasicInfoReport", + "directCcuBasicInfo_", + "directCcuBasicInfoReport_", + ]: + with self.subTest(leaked=leaked): + self.assertNotIn(leaked, comm_header) + self.assertNotIn(leaked, backend_header) + + self.assertIn("int TileXRCcuRuntimeSession::RefreshDirectCcuBasicInfo", runtime_session_source) + self.assertIn("bool TileXRCcuRuntimeSession::HasDirectCcuBasicInfo", runtime_session_source) + self.assertIn("ccuDirectRuntime_->QueryBasicInfo", runtime_session_source) + self.assertIn("direct CCU basic info cached", runtime_session_source) + self.assertIn("ResetDirectCcuBasicInfo", runtime_session_source) + self.assertIn("ResetDirectCcuBasicInfo();", runtime_session_source) + self.assertNotIn("udmaTransport_->" + "QueryCcuBasicInfo", comm_source + "\n" + backend_source + "\n" + runtime_session_source) + + for forbidden in [ + "TileXRCcuPrepareSubmitTasks", + "TileXRCcuSubmitTask", + "rtCCULaunch", + "HcclGetCcuTaskInfo", + "HcomGetCcuTaskInfo", + "libhcomm", + "libhccl_v2", + ]: + self.assertNotIn(forbidden, comm_header + "\n" + comm_source + "\n" + backend_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") + backend_header = CCU_BACKEND_HEADER.read_text(encoding="utf-8") + backend_source = CCU_BACKEND_SOURCE.read_text(encoding="utf-8") + planner_header = CCU_PLANNER_HEADER.read_text(encoding="utf-8") + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + runtime_session_source = CCU_RUNTIME_SESSION_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('ccu/tilexr_ccu_direct_orchestrator.h', planner_header + "\n" + planner_source) + self.assertIn('ccu/tilexr_ccu_direct_runtime.h', CCU_RUNTIME_SESSION_HEADER.read_text(encoding="utf-8")) + for leaked in [ + "PrepareDirectCcuInstallAttempt", + "FillDirectCcuLowerLayerPlanFromAllocation", + "PrepareDirectCcuLowerLayerPlanCallback", + ]: + with self.subTest(leaked=leaked): + self.assertNotIn(leaked, comm_header) + self.assertNotIn(leaked, backend_header) + self.assertIn("int CreateDriverAdapter(", runtime_header) + + self.assertIn("int TileXRCcuCollectivePlanner::PrepareDirectCcuInstallAttempt", planner_source) + self.assertIn("int TileXRCcuCollectivePlanner::FillDirectCcuLowerLayerPlanFromAllocation", planner_source) + self.assertIn("int TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback", planner_source) + self.assertIn("ccuDirectRuntime_->CreateDriverAdapter", runtime_session_source) + self.assertIn("TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode)", planner_source) + self.assertIn("next.lowerLayerPlan = nullptr", planner_source) + self.assertIn( + "next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback", + planner_source, + ) + self.assertIn("next.lowerLayerPlanUserData = &callbackContext", planner_source) + self.assertIn("TileXRCcuRunDirectInstallAttempt(next, attempt, report)", planner_source) + self.assertRegex( + planner_header, + r"(?s)#ifdef TILEXR_CCU_TESTING.*PrepareDirectCcuMemoryCopyInstallAttempt.*#endif", + ) + self.assertRegex( + planner_source, + r"(?s)#ifdef TILEXR_CCU_TESTING.*PrepareDirectCcuMemoryCopyInstallAttempt.*#endif", + ) + self.assertIn("int TileXRCcuDirectRuntime::CreateDriverAdapter", runtime_source) + self.assertNotIn("udmaTransport_->" + "CreateCcuDriverAdapter", comm_source + "\n" + backend_source + "\n" + runtime_session_source) + + combined = ( + comm_header + "\n" + comm_source + "\n" + backend_source + "\n" + + planner_source + "\n" + runtime_session_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_device_init_failure(self): + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("g_ccuDirectRuntimeUnavailableByDevice", runtime_session_source) + init_body = runtime_session_source[ + runtime_session_source.index("int TileXRCcuRuntimeSession::Init("): + runtime_session_source.index("void TileXRCcuRuntimeSession::ResetDirectCcuBasicInfo") + ] + prepare_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuInstallAttempt"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuMemoryCopyInstallAttempt") + ] + + self.assertIn("g_ccuDirectRuntimeUnavailableByDevice[devId_] = runtimeReport.message", init_body) + self.assertIn("direct CCU runtime unavailable for device", runtime_session_source) + self.assertIn("session.DirectCcuRuntimeUnavailableMessage()", prepare_body) + self.assertLess( + prepare_body.index("session.DirectCcuRuntimeUnavailableMessage()"), + prepare_body.index("RefreshDirectCcuBasicInfo(installDieId)"), + ) + + def test_tilexr_comm_direct_ccu_runtime_init_serializes_ra_initialization(self): + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + init_body = runtime_session_source[ + runtime_session_source.index("int TileXRCcuRuntimeSession::Init("): + runtime_session_source.index("void TileXRCcuRuntimeSession::ResetDirectCcuBasicInfo") + ] + + 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(runtimeOptions, &runtimeReport)") + unavailable_set_pos = init_body.index("g_ccuDirectRuntimeUnavailableByDevice[devId_] = runtimeReport.message") + + 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): + planner_source = CCU_PLANNER_SOURCE.read_text(encoding="utf-8") + prepare_body = planner_source[ + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuInstallAttempt"): + planner_source.index("int TileXRCcuCollectivePlanner::PrepareDirectCcuMemoryCopyInstallAttempt") + ] + + self.assertIn("TILEXR_CCU_DIRECT_INSTALL_DIE_ID", planner_source) + self.assertIn("SelectDirectCcuInstallDieId", planner_source) + self.assertIn("RefreshDirectCcuBasicInfo(installDieId)", prepare_body) + self.assertIn("basicInfo->dieId != installDieId", prepare_body) + self.assertNotIn("RefreshDirectCcuBasicInfo(0)", prepare_body) + + def test_tilexr_comm_direct_ccu_thread_allgather_aborts_after_device_init_failure(self): + runtime_session_source = CCU_RUNTIME_SESSION_SOURCE.read_text(encoding="utf-8") + thread_allgather_body = runtime_session_source[ + runtime_session_source.index("int TileXRCcuRuntimeSession::DirectCcuThreadAllGather"): + runtime_session_source.index("} // namespace TileXR") + ] + + self.assertIn("DirectCcuRuntimeUnavailableMessage()", thread_allgather_body) + self.assertIn("direct CCU thread allgather abort", thread_allgather_body) + self.assertLess( + thread_allgather_body.index("DirectCcuRuntimeUnavailableMessage()"), + thread_allgather_body.index("TILEXR_INIT_TIMEOUT"), + ) + + def test_tilexr_comm_direct_ccu_lower_layer_plan_api_is_header_visible(self): + comm_header = COMM_HEADER_FILE.read_text(encoding="utf-8") + for leaked in [ + "ConfigureDirectCcuLowerLayerTemplate", + "ConfigureDirectCcuLowerLayerTemplateFromAllocation", + "PrepareDirectCcuLowerLayerTemplateFromAllocation", + "RefreshDirectCcuLowerLayerPlan", + "HasDirectCcuLowerLayerPlan", + "GetDirectCcuLowerLayerPlanStatus", + "GetDirectCcuLowerLayerPlanReport", + "GetDirectCcuLowerLayerPlan", + "RefreshDirectCcuBasicInfo", + "HasDirectCcuBasicInfo", + "GetDirectCcuBasicInfoStatus", + "GetDirectCcuBasicInfo", + "GetDirectCcuBasicInfoReport", + "ConfigureDirectCcuVerifiedEndpointRoutes", + "ConfigureDirectCcuLocalVerifiedEndpointRoute", + "PrepareDirectCcuInstallAttempt", + ]: + with self.subTest(leaked=leaked): + self.assertNotIn(leaked, comm_header) + + code = textwrap.dedent( + r''' + #include "tilexr_comm.h" + + #include + + using namespace TileXR; + + int main() + { + using InitFn = int (TileXRComm::*)(); + using GetterFn = TileXRCcuBackend* (TileXRComm::*)(); + using ConstGetterFn = const TileXRCcuBackend* (TileXRComm::*)() const; + using EnableForTestFn = int (TileXRComm::*)(); + + InitFn init = &TileXRComm::InitCcuBackend; + GetterFn getter = &TileXRComm::GetCcuBackendForCollectives; + ConstGetterFn constGetter = &TileXRComm::GetCcuBackendForCollectives; + EnableForTestFn enableForTest = &TileXRComm::EnableCcuBackendForTest; + (void)init; + (void)getter; + (void)constGetter; + (void)enableForTest; + 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 00000000..29868acb --- /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 00000000..b61e29be --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_microcode.py @@ -0,0 +1,590 @@ +#!/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; + } + + TileXRCcuInstr local; + if (TileXRCcuEncodeTransLocMemToLocMem(spec, &local) != TILEXR_SUCCESS) { + std::cerr << "trans loc->loc encode failed\n"; + return 5; + } + if (local.words[0] != 0x010102020201100aULL || + local.words[1] != 0x5a00001203010102ULL || + local.words[2] != 0x0007000000000000ULL || + local.words[3] != 0x0003040200020401ULL) { + std::cerr << "unexpected trans loc->loc words\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_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 || + TileXRCcuEncodeTransLocMemToLocMem(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 || + TileXRCcuEncodeTransLocMemToLocMem(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 || + TileXRCcuEncodeTransLocMemToLocMem(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("TileXRCcuEncodeTransLocMemToLocMem", 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("0x100aU", 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 00000000..fc61ea29 --- /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 00000000..797bb62f --- /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 00000000..49eff720 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_public_api_compile_probe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +class TileXRCcuPublicApiCompileProbeTest(unittest.TestCase): + def test_external_direct_ccu_public_probe_removed(self): + self.assertFalse((REPO_ROOT / "tests" / "ccu" / "ccu_public_direct_api_compile_probe.c").exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/ccu/test_tilexr_ccu_public_comm_api.py b/tests/ccu/test_tilexr_ccu_public_comm_api.py new file mode 100644 index 00000000..db54ace9 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_public_comm_api.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import unittest +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +PUBLIC_HEADERS = [ + REPO_ROOT / "src" / "include" / "tilexr_api.h", + REPO_ROOT / "src" / "include" / "tilexr_types.h", + REPO_ROOT / "src" / "include" / "tilexr_collectives.h", +] +CORE_API_HEADER = REPO_ROOT / "src" / "include" / "tilexr_api.h" +COMM_WRAP = REPO_ROOT / "src" / "comm" / "comm_wrap.cpp" + + +class TileXRCcuPublicCommApiTest(unittest.TestCase): + def test_core_api_header_has_no_ccu_symbols(self): + header = CORE_API_HEADER.read_text(encoding="utf-8") + for needle in ["CCU", "Ccu", "DirectCcu", "TILEXR_DIRECT_CCU"]: + with self.subTest(needle=needle): + self.assertNotIn(needle, header) + + def test_installed_public_headers_do_not_expose_low_level_ccu_model(self): + forbidden = [ + "TileXRDirectCcu", + "PrepareDirectCcu", + "SubmitPrepared", + "Repository", + "SQE", + " XN", + " CKE", + "TaskInfo", + "rtCCULaunch", + "rtCcuTaskInfo_t", + "hcomm", + "hccl", + ] + for path in PUBLIC_HEADERS: + text = path.read_text(encoding="utf-8") + for needle in forbidden: + with self.subTest(path=path.name, needle=needle): + self.assertNotIn(needle, text) + + def test_collectives_header_only_exposes_high_level_backend_names(self): + text = (REPO_ROOT / "src" / "include" / "tilexr_collectives.h").read_text(encoding="utf-8") + for needle in [ + "TILEXR_COLLECTIVE_BACKEND_AUTO", + "TILEXR_COLLECTIVE_BACKEND_AIV", + "TILEXR_COLLECTIVE_BACKEND_UDMA", + "TILEXR_COLLECTIVE_BACKEND_CCU", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, text) + + def test_comm_wrap_has_no_direct_ccu_public_bridge(self): + wrapper = COMM_WRAP.read_text(encoding="utf-8") + for needle in [ + "TileXRCommInitRankDirectCcuWithDomain", + "TileXRCommPrepareDirectCcu", + "TileXRCommPrepareDirectCcuMemoryCopy", + "TileXRDirectCcuGetPreparedTask", + "TileXRDirectCcuSubmitPrepared", + "TileXRCommReadDirectCcuInstructions", + "TileXRDirectCcuDestroyPrepared", + "TileXRDirectCcuPreparedTasks", + ]: + with self.subTest(needle=needle): + self.assertNotIn(needle, wrapper) + + +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 00000000..28d1b96c --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_ra_custom_channel_loader.py @@ -0,0 +1,2191 @@ +#!/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" +TOPOLOGY_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_topology.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 [] + if DIRECT_RUNTIME_SOURCE in extra_sources and TOPOLOGY_SOURCE not in extra_sources: + extra_sources = [*extra_sources, TOPOLOGY_SOURCE] + 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", + "TileXRCcuRaGetTpAttrAsyncFunc", + "TileXRCcuRaSetTpAttrAsyncFunc", + "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", + "RaGetTpAttrAsync", + "RaSetTpAttrAsync", + "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 mappedJettyPriority", create_body) + self.assertNotIn("qpAttr.ub.priority = 2", create_body) + + def test_direct_runtime_maps_ctp_jetty_priority_without_setting_tp_sl(self): + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + select_start = source.index("int TileXRCcuDirectRuntime::SelectTpRouteForPeer(") + select_body = source[ + select_start: + source.index("int TileXRCcuDirectRuntime::QueryTpHandleForPeer(", select_start) + ] + compact_body = " ".join(select_body.split()) + + self.assertNotIn( + "if (tpType == TILEXR_CCU_HCCP_TP_TYPE_CTP) {", + select_body, + ) + self.assertIn("RaGetTpAttrAsync(", select_body) + self.assertIn("MapQosToTpAndSl(", select_body) + self.assertIn( + "if (tpType == TILEXR_CCU_HCCP_TP_TYPE_RTP) { TileXRCcuHccpTpAttr setAttr", + compact_body, + ) + self.assertIn("*mappedJettyPriority = mappedSl", select_body) + + def test_peer_endpoint_route_uses_the_driver_returned_jetty_id(self): + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + prepare_body = source[ + source.index("int TileXRCcuDirectRuntime::PreparePeerEndpointRoutes("): + source.index("int TileXRCcuDirectRuntime::QueryTpHandleForPeer(") + ] + + self.assertIn( + "state.route.startJettyId = static_cast(state.qpInfo.ub.id)", + prepare_body, + ) + self.assertNotIn( + "state.route.startJettyId = static_cast(TILEXR_CCU_DIRECT_LOOP_JETTY_ID + ordinal)", + prepare_body, + ) + + + def test_direct_ccu_runtime_keeps_ra_custom_channel_provider_alive_for_created_adapters(self): + header = DIRECT_RUNTIME_HEADER.read_text(encoding="utf-8") + source = DIRECT_RUNTIME_SOURCE.read_text(encoding="utf-8") + + self.assertIn("tilexr_ccu_ra_custom_channel_provider.h", header) + self.assertIn("TileXRCcuRaCustomChannelProvider raCustomChannelProvider_", header) + self.assertIn("raCustomChannelProvider_.Init(devicePhyId_, loader_.RaCustomChannel", source) + self.assertIn("raCustomChannelProvider_.CreateAdapter(adapter, report)", source) + self.assertNotIn("TileXRCcuRaCustomChannelProvider provider;", source) + + def test_ccu_hccp_loader_keeps_private_hcomm_hccl_dependencies_out(self): + combined = ( + ABI_HEADER.read_text(encoding="utf-8") + + "\n" + + TYPES_HEADER.read_text(encoding="utf-8") + + "\n" + + LOADER_HEADER.read_text(encoding="utf-8") + + "\n" + + LOADER_SOURCE.read_text(encoding="utf-8") + ) + + self.assertIn("libra.so", combined) + self.assertIn("libruntime.so", combined) + self.assertIn("TILEXR_CCU_EID_BYTES", ABI_HEADER.read_text(encoding="utf-8")) + self.assertIn('tilexr_ccu_abi_constants.h', TYPES_HEADER.read_text(encoding="utf-8")) + self.assertNotIn('tilexr_ccu_lower_layer_payloads.h', TYPES_HEADER.read_text(encoding="utf-8")) + for needle in [ + "udma/", + "#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[15] != 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 00000000..5b74a0cb --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_ra_custom_channel_provider.py @@ -0,0 +1,325 @@ +#!/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_retries_transient_roce_eagain(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + + #include + + using namespace TileXR; + + int g_calls = 0; + + int FakeRaCustomChannel( + TileXRCcuRaInfo, + TileXRCcuCustomChannelIn*, + TileXRCcuCustomChannelOut* out) + { + ++g_calls; + if (g_calls < 3) { + out->opRet = 99; + return 128101; + } + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x55; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0xabcdef01U; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x500000000ULL; + 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() + { + TileXRCcuRaCustomChannelProvider provider; + TileXRCcuRaCustomChannelProviderReport providerReport; + if (provider.Init(3, FakeRaCustomChannel, &providerReport) != TILEXR_SUCCESS) { + return 1; + } + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + if (provider.CreateAdapter(&adapter, &adapterReport) != TILEXR_SUCCESS) { + return 2; + } + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(0, &basic, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << adapterReport.message << "\n"; + return 3; + } + if (g_calls != 3 || basic.msId != 0x55 || basic.missionKey != 0xabcdef01U) { + 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_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 00000000..f7d60de2 --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_resource_allocator.py @@ -0,0 +1,1017 @@ +#!/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 != 1991 || + 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.remoteXn.num != 24 || + 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 != 24 || 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; + request.minimumLocalXnCount = 3; + request.minimumRemoteXnCount = 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.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 != 3 || + allocation.localXn.startId != 1 || allocation.localXn.num != 3 || + allocation.remoteXn.startId != 4 || allocation.remoteXn.num != 3 || + allocation.repository.startId != 1 || allocation.repository.num != 2) { + std::cerr << "pure barrier allocation mismatch\n"; + return 5; + } + if (report.localXnAllocated != 3 || + report.remoteXnAllocated != 3 || + 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_release_reclaims_latest_receipt_and_rejects_non_lifo_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 = 10; + spec.missionCount = 4; + spec.instructionStartId = 100; + spec.instructionCount = 64; + spec.xnStartId = 200; + spec.xnCount = 16; + spec.ckeStartId = 300; + spec.ckeCount = 8; + spec.channelStartId = 20; + spec.channelCount = 4; + + TileXRCcuResourceRequest request; + request.sqeArgCount = 0; + request.syncResourceCount = 1; + request.syncInstructionCount = 2; + request.bindingsPerSyncResource = 1; + + TileXRCcuResourceAllocator allocator; + if (allocator.Init(spec) != TILEXR_SUCCESS) { + std::cerr << "init failed\n"; + return 1; + } + + TileXRCcuProducerPlan firstPlan; + TileXRCcuProducerPlan secondPlan; + TileXRCcuProducerPlan thirdPlan; + TileXRCcuResourceAllocation first; + TileXRCcuResourceAllocation second; + TileXRCcuResourceAllocation third; + TileXRCcuResourceAllocatorReport report; + if (allocator.Allocate(request, &firstPlan, &first, &report) != TILEXR_SUCCESS || + allocator.Allocate(request, &secondPlan, &second, &report) != TILEXR_SUCCESS) { + std::cerr << "initial allocate failed: " << report.message << "\n"; + return 2; + } + if (first.mission.startId != 10 || second.mission.startId != 11 || + first.repository.startId != 100 || second.repository.startId != 102 || + first.localXn.startId == second.localXn.startId || + first.channels.startId == second.channels.startId) { + std::cerr << "initial allocations did not advance resource cursors\n"; + return 3; + } + if (allocator.Release(first.receiptId) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "non-LIFO release was accepted\n"; + return 4; + } + if (allocator.Release(second.receiptId) != TILEXR_SUCCESS) { + std::cerr << "latest release failed\n"; + return 5; + } + if (allocator.Allocate(request, &thirdPlan, &third, &report) != TILEXR_SUCCESS) { + std::cerr << "third allocate failed after release: " << report.message << "\n"; + return 6; + } + if (third.mission.startId != second.mission.startId || + third.repository.startId != second.repository.startId || + third.localXn.startId != second.localXn.startId || + third.remoteXn.startId != second.remoteXn.startId || + third.localWaitCke.startId != second.localWaitCke.startId || + third.remoteNotifyCke.startId != second.remoteNotifyCke.startId || + third.channels.startId != second.channels.startId) { + std::cerr << "released resource ranges were not reused\n"; + return 7; + } + if (allocator.Release(first.receiptId) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "old non-tail receipt release should still be rejected\n"; + return 8; + } + if (allocator.Release(third.receiptId) != TILEXR_SUCCESS || + allocator.Release(first.receiptId) != TILEXR_SUCCESS) { + std::cerr << "tail releases failed\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_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 00000000..d9d4c43e --- /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_signal_wait_program.py b/tests/ccu/test_tilexr_ccu_signal_wait_program.py new file mode 100644 index 00000000..f008749b --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_signal_wait_program.py @@ -0,0 +1,264 @@ +#!/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] +SIGNAL_WAIT_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_signal_wait_program.h" +SIGNAL_WAIT_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_signal_wait_program.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 TileXRCcuSignalWaitProgramTest(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; remote CANN compile covers signal/wait C++ syntax") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "signal_wait_program_test.cpp" + test_bin = temp_path / "signal_wait_program_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(SIGNAL_WAIT_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_signal_wait_program_initializes_source_cke_before_signal_only_post(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_signal_wait_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuSignalWaitProgramSpec spec; + spec.role = TileXRCcuSignalWaitProgramRole::Signal; + spec.channelId = 2; + spec.remoteXn = 2361; + spec.localXn = 1961; + spec.remoteNotifyCke = 364; + spec.remoteNotifyMask = 1; + spec.sourceCke = 0x101; + spec.sourceCkeMask = 0xffff; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildSignalWaitProgram(spec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "signal program build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 5 || report.postInstructionCount != 1 || + report.waitInstructionCount != 0 || report.totalInstructionCount != 5) { + std::cerr << "unexpected signal report\n"; + return 2; + } + if (program[2].words[0] != 0xffff010100010802ULL || + program[3].words[0] != 0xffff0101016c100bULL || + program[3].words[1] != 0x0000000000000002ULL || + program[3].words[2] != 0x0001000000000000ULL) { + std::cerr << "unexpected signal instructions\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_signal_wait_program_builds_wait_only_clear_cke_wait(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_signal_wait_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuSignalWaitProgramSpec spec; + spec.role = TileXRCcuSignalWaitProgramRole::Wait; + spec.localXn = 0x7a9; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildSignalWaitProgram(spec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "wait program build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 1 || report.postInstructionCount != 0 || + report.waitInstructionCount != 1 || report.totalInstructionCount != 1) { + std::cerr << "unexpected wait report\n"; + return 2; + } + if (program[0].words[0] != 0x0000000000010804ULL || + program[0].words[1] != 0x0000000000010220ULL) { + std::cerr << "unexpected wait instruction\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_signal_wait_program_builds_signal_and_wait_for_barrier(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_signal_wait_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + TileXRCcuSignalWaitProgramSpec spec; + spec.role = TileXRCcuSignalWaitProgramRole::SignalAndWait; + spec.channelId = 2; + spec.remoteXn = 2361; + spec.localXn = 1961; + spec.remoteNotifyCke = 364; + spec.remoteNotifyMask = 1; + spec.localWaitCke = 0x220; + spec.localWaitMask = 1; + spec.sourceCke = 0x101; + spec.sourceCkeMask = 0xffff; + + std::vector program; + TileXRCcuBarrierProgramReport report; + if (TileXRCcuBuildSignalWaitProgram(spec, &program, &report) != TILEXR_SUCCESS) { + std::cerr << "barrier program build failed: " << report.message << "\n"; + return 1; + } + if (program.size() != 6 || report.postInstructionCount != 1 || + report.waitInstructionCount != 1 || report.totalInstructionCount != 6) { + std::cerr << "unexpected signal_and_wait report\n"; + return 2; + } + if (program[2].words[0] != 0xffff010100010802ULL || + program[3].words[0] != 0xffff0101016c100bULL || + program[4].words[0] != 0x0000000000010804ULL || + program[4].words[1] != 0x0000000000010220ULL) { + std::cerr << "unexpected signal_and_wait instructions\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_signal_wait_program_rejects_missing_role_resources(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_signal_wait_program.h" + + #include + #include + + using namespace TileXR; + + int main() + { + std::vector program(1); + TileXRCcuBarrierProgramReport report; + TileXRCcuSignalWaitProgramSpec spec; + spec.role = TileXRCcuSignalWaitProgramRole::Signal; + if (TileXRCcuBuildSignalWaitProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing signal resources accepted\n"; + return 1; + } + if (!program.empty() || report.message.find("signal") == std::string::npos) { + std::cerr << "weak signal diagnostic: " << report.message << "\n"; + return 2; + } + spec.role = TileXRCcuSignalWaitProgramRole::Wait; + if (TileXRCcuBuildSignalWaitProgram(spec, &program, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "missing wait resources accepted\n"; + return 3; + } + if (!program.empty() || report.message.find("wait") == std::string::npos) { + std::cerr << "weak 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_signal_wait_program_is_wired(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = SIGNAL_WAIT_HEADER.read_text(encoding="utf-8") + source = SIGNAL_WAIT_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_signal_wait_program.h", cmake) + self.assertIn("ccu/tilexr_ccu_signal_wait_program.cpp", cmake) + self.assertIn("enum class TileXRCcuSignalWaitProgramRole", header) + self.assertIn("struct TileXRCcuSignalWaitProgramSpec", header) + self.assertIn("TileXRCcuBuildSignalWaitProgram", header) + self.assertIn("TileXRCcuEncodeLoadImdToXn", source) + self.assertIn("TileXRCcuEncodeSetCke", source) + self.assertIn("TileXRCcuEncodeSyncCke", source) + self.assertIn("TileXRCcuEncodeClearCke", 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 00000000..2f45fd5f --- /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 00000000..4760eb98 --- /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 + +namespace { + +static_assert(TILEXR_COLLECTIVE_BACKEND_AUTO == 0, "AUTO must be zero for zero-initialized options"); +static_assert(TILEXR_COLLECTIVE_BACKEND_AIV == 1, "AIV enum value changed"); +static_assert(TILEXR_COLLECTIVE_BACKEND_UDMA == 2, "UDMA enum value changed"); +static_assert(TILEXR_COLLECTIVE_BACKEND_CCU == 3, "CCU enum value changed"); + +int CheckFunctionPointers() +{ + TileXRCollectiveOptions options {}; + if (options.backend != TILEXR_COLLECTIVE_BACKEND_AUTO) { + return 1; + } + + auto allGather = &TileXRAllGatherEx; + auto allToAll = &TileXRAllToAllEx; + auto allReduce = &TileXRAllReduceEx; + auto reduceScatter = &TileXRReduceScatterEx; + auto broadcast = &TileXRBroadcastEx; + auto profileProbe = &TileXRProfileProbeEx; + + (void)allGather; + (void)allToAll; + (void)allReduce; + (void)reduceScatter; + (void)broadcast; + (void)profileProbe; + return 0; +} + +int CheckBackendDispatch() +{ + using TileXRCollectives::Host::BackendTestState; + using TileXRCollectives::Host::CollectiveRequest; + using TileXRCollectives::Host::DispatchCollective; + using TileXRCollectives::Host::ResetBackendTestState; + using TileXRCollectives::Host::SetBackendTestState; + + CollectiveRequest request {}; + request.type = TileXR::TileXRType::ALL_GATHER; + request.sendBuf = reinterpret_cast(0x1000); + request.recvBuf = reinterpret_cast(0x2000); + request.count = 1; + request.dataType = TileXR::TILEXR_DATA_TYPE_INT32; + request.comm = reinterpret_cast(0x3000); + request.stream = nullptr; + int sendValue = 1; + int recvValue = 0; + TileXRCollectiveOptions options {}; + options.backend = TILEXR_COLLECTIVE_BACKEND_AUTO; + + BackendTestState state {}; + state.aivReturn = TileXR::TILEXR_SUCCESS; + state.udmaInitialized = false; + state.ccuInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_AUTO) != TileXR::TILEXR_SUCCESS) { + return 2; + } + + state.udmaInitialized = true; + state.udmaSupported = true; + state.udmaReturn = TileXR::TILEXR_SUCCESS; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_UDMA) != TileXR::TILEXR_SUCCESS) { + return 3; + } + + state.udmaInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_UDMA) != TileXR::TILEXR_ERROR_NOT_INITIALIZED) { + return 4; + } + + state.udmaInitialized = true; + state.udmaSupported = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_UDMA) != TileXR::TILEXR_ERROR_NOT_SUPPORT) { + return 5; + } + + state.ccuInitialized = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_ERROR_NOT_INITIALIZED) { + return 6; + } + + state.ccuInitialized = true; + state.ccuSupported = false; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_ERROR_NOT_SUPPORT) { + return 7; + } + + state.ccuSupported = true; + state.ccuReturn = TileXR::TILEXR_SUCCESS; + SetBackendTestState(state); + if (DispatchCollective(request, TILEXR_COLLECTIVE_BACKEND_CCU) != TileXR::TILEXR_SUCCESS) { + return 8; + } + + options.backend = TILEXR_COLLECTIVE_BACKEND_UDMA; + state.udmaInitialized = false; + state.udmaSupported = false; + SetBackendTestState(state); + if (TileXRAllGatherEx(&sendValue, &recvValue, 1, TileXR::TILEXR_DATA_TYPE_INT32, request.comm, nullptr, &options) != + TileXR::TILEXR_ERROR_NOT_INITIALIZED) { + return 9; + } + + options.backend = TILEXR_COLLECTIVE_BACKEND_CCU; + state.ccuInitialized = true; + state.ccuSupported = false; + SetBackendTestState(state); + if (TileXRAllGatherEx(&sendValue, &recvValue, 1, TileXR::TILEXR_DATA_TYPE_INT32, request.comm, nullptr, &options) != + TileXR::TILEXR_ERROR_NOT_SUPPORT) { + return 10; + } + + ResetBackendTestState(); + return 0; +} + +} // namespace + +int main() +{ + const int pointerRet = CheckFunctionPointers(); + if (pointerRet != 0) { + return pointerRet; + } + return CheckBackendDispatch(); +} diff --git a/tests/comm/unit/test_tilexr_source_guards.cpp b/tests/comm/unit/test_tilexr_source_guards.cpp index 4c7e70e0..84f9896b 100644 --- a/tests/comm/unit/test_tilexr_source_guards.cpp +++ b/tests/comm/unit/test_tilexr_source_guards.cpp @@ -2,6 +2,7 @@ #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,713 @@ 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, "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 ccuBackendHeaderPath = "src/comm/ccu/tilexr_ccu_backend.h"; + const std::string ccuBackendSourcePath = "src/comm/ccu/tilexr_ccu_backend.cpp"; + const std::string ccuRuntimeSessionHeaderPath = "src/comm/ccu/tilexr_ccu_runtime_session.h"; + const std::string ccuRuntimeSessionSourcePath = "src/comm/ccu/tilexr_ccu_runtime_session.cpp"; + const std::string ccuPlannerHeaderPath = "src/comm/ccu/tilexr_ccu_collective_planner.h"; + const std::string ccuPlannerSourcePath = "src/comm/ccu/tilexr_ccu_collective_planner.cpp"; + const std::string ccuExecutorHeaderPath = "src/comm/ccu/tilexr_ccu_executor.h"; + const std::string ccuExecutorSourcePath = "src/comm/ccu/tilexr_ccu_executor.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 ccuBackendHeaderText = ReadFile(ccuBackendHeaderPath); + const auto ccuBackendSourceText = ReadFile(ccuBackendSourcePath); + const auto ccuRuntimeSessionHeaderText = ReadFile(ccuRuntimeSessionHeaderPath); + const auto ccuRuntimeSessionSourceText = ReadFile(ccuRuntimeSessionSourcePath); + const auto ccuPlannerHeaderText = ReadFile(ccuPlannerHeaderPath); + const auto ccuPlannerSourceText = ReadFile(ccuPlannerSourcePath); + const auto ccuExecutorHeaderText = ReadFile(ccuExecutorHeaderPath); + const auto ccuExecutorSourceText = ReadFile(ccuExecutorSourcePath); + const auto ccuRuntimeHeaderText = ReadFile(ccuRuntimeHeaderPath); + const auto ccuRuntimeSourceText = ReadFile(ccuRuntimeSourcePath); + + CheckContains(commHeaderPath, commHeaderText, "class TileXRCcuBackend;"); + CheckContains(commHeaderPath, commHeaderText, "std::unique_ptr ccuBackend_"); + for (const auto& forbiddenCommDetail : { + "tilexr_ccu_direct_runtime.h", + "tilexr_ccu_lower_layer_plan_builder.h", + "TileXRCcuDirectRuntime", + "directCcuBasicInfo_", + "directCcuLowerLayerPlan_", + "directCcuVerifiedEndpointRoutes_", + "PrepareDirectCcuInstallAttempt", + "PrepareDirectCcuLowerLayerPlanCallback", + }) { + CheckNotContains(commHeaderPath, commHeaderText, forbiddenCommDetail); + } + + CheckContains(ccuBackendHeaderPath, ccuBackendHeaderText, "class TileXRCcuBackend"); + CheckContains(ccuBackendHeaderPath, ccuBackendHeaderText, "TileXRSockExchange *exchange"); + CheckContains(ccuBackendHeaderPath, ccuBackendHeaderText, "std::unique_ptr impl_"); + CheckContains(ccuBackendSourcePath, ccuBackendSourceText, "#include \"ccu/tilexr_ccu_runtime_session.h\""); + CheckContains(ccuBackendSourcePath, ccuBackendSourceText, "#include \"ccu/tilexr_ccu_collective_planner.h\""); + CheckContains(ccuBackendSourcePath, ccuBackendSourceText, "#include \"ccu/tilexr_ccu_executor.h\""); + CheckNotContains(ccuBackendSourcePath, ccuBackendSourceText, "#include \"ccu/tilexr_ccu_direct_runtime.h\""); + CheckNotContains(ccuBackendSourcePath, ccuBackendSourceText, "#include \"ccu/tilexr_ccu_repository.h\""); + CheckNotContains(ccuBackendSourcePath, ccuBackendSourceText, "TileXRCcuDirectRuntime"); + CheckContains(ccuBackendSourcePath, ccuBackendSourceText, "TileXRCcuBackend::Impl::Init"); + CheckContains(ccuRuntimeSessionHeaderPath, ccuRuntimeSessionHeaderText, "TileXRCcuDirectRuntime"); + CheckContains(ccuRuntimeSessionSourcePath, ccuRuntimeSessionSourceText, "ccuDirectRuntime_->CreateDriverAdapter"); + CheckContains(ccuPlannerHeaderPath, ccuPlannerHeaderText, "class TileXRCcuRuntimeSession"); + CheckContains(ccuPlannerHeaderPath, ccuPlannerHeaderText, "#ifdef TILEXR_CCU_TESTING"); + CheckContains(ccuPlannerHeaderPath, ccuPlannerHeaderText, "PrepareDirectCcuMemoryCopyInstallAttempt"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "#include \"ccu/tilexr_ccu_repository.h\""); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "#ifdef TILEXR_CCU_TESTING"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "TileXRCcuCollectivePlanner::PrepareDirectCcuInstallAttempt"); + CheckContains( + ccuPlannerSourcePath, + ccuPlannerSourceText, + "TileXRCcuCollectivePlanner::PrepareDirectCcuMemoryCopyInstallAttempt"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback"); + CheckContains( + ccuPlannerSourcePath, + ccuPlannerSourceText, + "TileXRCcuMakeRepositoryDeviceMemoryOps(next.repositoryMemoryAllocMode)"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "next.lowerLayerPlan = nullptr"); + CheckContains( + ccuPlannerSourcePath, + ccuPlannerSourceText, + "next.prepareLowerLayerPlan = &TileXRCcuCollectivePlanner::PrepareDirectCcuLowerLayerPlanCallback"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "LowerLayerPlanCallbackContext callbackContext"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "next.lowerLayerPlanUserData = &callbackContext"); + CheckContains(ccuPlannerSourcePath, ccuPlannerSourceText, "TileXRCcuRunDirectInstallAttempt(next, attempt, report)"); + CheckContains(ccuExecutorHeaderPath, ccuExecutorHeaderText, "#ifdef TILEXR_CCU_TESTING"); + CheckContains(ccuExecutorHeaderPath, ccuExecutorHeaderText, "ReadDirectCcuInstructionsForDebug"); + CheckContains(ccuExecutorSourcePath, ccuExecutorSourceText, "#ifdef TILEXR_CCU_TESTING"); + CheckContains(ccuExecutorSourcePath, ccuExecutorSourceText, "ReadDirectCcuInstructionsForDebug"); + 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 initCcuBackendBegin = commSourceText.find("int TileXRComm::InitCcuBackend"); + if (initUdmaBegin == std::string::npos || initCcuBackendBegin == std::string::npos || + initUdmaBegin >= initCcuBackendBegin) { + std::cerr << commSourcePath << ": cannot isolate InitUDMA body" << std::endl; + ++g_failures; + } else { + const auto initUdmaBody = commSourceText.substr(initUdmaBegin, initCcuBackendBegin - 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(ccuBackendHeaderPath, ccuBackendHeaderText, forbidden); + CheckNotContains(ccuBackendSourcePath, ccuBackendSourceText, forbidden); + CheckNotContains(ccuRuntimeSessionHeaderPath, ccuRuntimeSessionHeaderText, forbidden); + CheckNotContains(ccuRuntimeSessionSourcePath, ccuRuntimeSessionSourceText, forbidden); + CheckNotContains(ccuPlannerHeaderPath, ccuPlannerHeaderText, forbidden); + CheckNotContains(ccuPlannerSourcePath, ccuPlannerSourceText, forbidden); + CheckNotContains(ccuExecutorHeaderPath, ccuExecutorHeaderText, forbidden); + CheckNotContains(ccuExecutorSourcePath, ccuExecutorSourceText, 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 +1014,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;