From a50245a8a5c8d38b00d0d4a0e7d0a835f9abd628 Mon Sep 17 00:00:00 2001 From: Kur0x Date: Tue, 28 Jul 2026 10:56:50 +0800 Subject: [PATCH] feat(ccu): add HCCP driver adapters --- src/comm/CMakeLists.txt | 9 + src/comm/ccu/tilexr_ccu_driver_adapter.cpp | 524 +++++++++ src/comm/ccu/tilexr_ccu_driver_adapter.h | 147 +++ src/comm/ccu/tilexr_ccu_hccp_loader.cpp | 581 ++++++++++ src/comm/ccu/tilexr_ccu_hccp_loader.h | 109 ++ src/comm/ccu/tilexr_ccu_hccp_types.h | 584 ++++++++++ .../ccu/tilexr_ccu_lower_layer_payloads.cpp | 172 +++ .../ccu/tilexr_ccu_lower_layer_payloads.h | 67 ++ .../tilexr_ccu_ra_custom_channel_provider.cpp | 112 ++ .../tilexr_ccu_ra_custom_channel_provider.h | 82 ++ .../ccu_lower_layer_payload_hcomm_oracle.cpp | 348 ++++++ tests/ccu/test_tilexr_ccu_driver_adapter.py | 995 ++++++++++++++++++ .../test_tilexr_ccu_lower_layer_payloads.py | 304 ++++++ ...r_ccu_lower_layer_payloads_hcomm_oracle.py | 73 ++ ...t_tilexr_ccu_ra_custom_channel_provider.py | 263 +++++ 15 files changed, 4370 insertions(+) create mode 100644 src/comm/ccu/tilexr_ccu_driver_adapter.cpp create mode 100644 src/comm/ccu/tilexr_ccu_driver_adapter.h create mode 100644 src/comm/ccu/tilexr_ccu_hccp_loader.cpp create mode 100644 src/comm/ccu/tilexr_ccu_hccp_loader.h create mode 100644 src/comm/ccu/tilexr_ccu_hccp_types.h create mode 100644 src/comm/ccu/tilexr_ccu_lower_layer_payloads.cpp create mode 100644 src/comm/ccu/tilexr_ccu_lower_layer_payloads.h create mode 100644 src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp create mode 100644 src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h create mode 100644 tests/ccu/ccu_lower_layer_payload_hcomm_oracle.cpp create mode 100644 tests/ccu/test_tilexr_ccu_driver_adapter.py create mode 100644 tests/ccu/test_tilexr_ccu_lower_layer_payloads.py create mode 100644 tests/ccu/test_tilexr_ccu_lower_layer_payloads_hcomm_oracle.py create mode 100644 tests/ccu/test_tilexr_ccu_ra_custom_channel_provider.py diff --git a/src/comm/CMakeLists.txt b/src/comm/CMakeLists.txt index 16c39a5b..00a7823f 100644 --- a/src/comm/CMakeLists.txt +++ b/src/comm/CMakeLists.txt @@ -108,12 +108,21 @@ set(TILEXR_SOURCE_FILE tilexr_comm.cpp ccu/tilexr_ccu_abi_constants.h ccu/tilexr_ccu_barrier_program.h ccu/tilexr_ccu_barrier_program.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_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_ra_custom_channel_provider.h + ccu/tilexr_ccu_ra_custom_channel_provider.cpp ccu/tilexr_ccu_resource_allocator.h ccu/tilexr_ccu_resource_allocator.cpp ccu/tilexr_ccu_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..3c2c1bb0 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_driver_adapter.h" + +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuDriverAdapterReport* report) +{ + if (report == nullptr) { + return; + } + *report = TileXRCcuDriverAdapterReport{}; +} + +int Fail(TileXRCcuDriverAdapterReport* report, const std::string& message, int code = TILEXR_ERROR_PARA_CHECK_FAIL) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +void InitRequest(uint8_t dieId, uint32_t opcode, TileXRCcuCustomChannelIn* in) +{ + std::memset(in, 0, sizeof(*in)); + in->op = opcode; + in->offsetStartIdx = 0; + in->data.dataInfo.udieIdx = dieId; +} + +void FillCallReport( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + int driverRet, + int opRet, + const std::string& message, + TileXRCcuDriverAdapterReport* report) +{ + if (report == nullptr) { + return; + } + report->devicePhyId = devicePhyId; + report->dieId = dieId; + report->opcode = opcode; + report->driverRet = driverRet; + report->opRet = opRet; + report->message = message; +} + +std::string CcuCustomChannelFailureMessage( + const char* prefix, + uint32_t opcode, + int driverRet, + int opRet) +{ + std::ostringstream message; + message << prefix + << " op=" << opcode + << " driverRet=" << driverRet + << " opRet=" << opRet; + return message.str(); +} + +template +void CopyPayloadToSlot(const Payload& payload, TileXRCcuDataTypeUnion* slot) +{ + std::memcpy(slot, payload.raw, sizeof(payload.raw)); +} + +bool DirectTraceEnabled() +{ + const char* value = std::getenv("TILEXR_CCU_DIRECT_TRACE"); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +uint64_t LoadWord(const void* data, uint32_t offset, uint32_t bytes) +{ + uint64_t word = 0; + if (offset >= bytes) { + return word; + } + const uint32_t copyBytes = std::min(sizeof(word), bytes - offset); + std::memcpy(&word, static_cast(data) + offset, copyBytes); + return word; +} + +void TraceWords(const char* label, const void* data, uint32_t bytes) +{ + const uint32_t wordCount = (bytes + 7U) / 8U; + std::cerr << "TileXRDirectCcuTrace " << label << "Words=" << wordCount; + for (uint32_t i = 0; i < wordCount; ++i) { + std::cerr << " w" << i << "=" << std::hex << std::showbase + << LoadWord(data, i * 8U, bytes) + << std::dec << std::noshowbase; + } + std::cerr << "\n"; +} + +void TraceCustomChannelRequest( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in) +{ + if (!DirectTraceEnabled()) { + return; + } + + const uint32_t payloadBytes = std::min( + in.data.dataInfo.dataLen == 0 ? TILEXR_CCU_DATA_ARRAY_SLOT_BYTES : in.data.dataInfo.dataLen, + sizeof(in.data.dataInfo.dataArray)); + std::cerr << "TileXRDirectCcuTrace customChannel" + << " devicePhyId=" << devicePhyId + << " op=" << opcode + << " dieId=" << static_cast(dieId) + << " requestDieId=" << in.data.dataInfo.udieIdx + << " offset=" << in.offsetStartIdx + << " dataLen=" << in.data.dataInfo.dataLen + << " arraySize=" << in.data.dataInfo.dataArraySize + << " payloadWords=" << ((payloadBytes + 7U) / 8U) + << "\n"; + TraceWords("customChannel.request", &in, std::min(sizeof(in), 256U)); + TraceWords("customChannel.requestTrailer", &in.offsetStartIdx, sizeof(in.offsetStartIdx) + sizeof(in.op)); + TraceWords("customChannel.payload", in.data.dataInfo.dataArray, payloadBytes); +} + +void TraceCustomChannelReturn( + uint32_t devicePhyId, + uint8_t dieId, + uint32_t opcode, + int driverRet, + const TileXRCcuCustomChannelOut& out) +{ + if (!DirectTraceEnabled()) { + return; + } + + std::cerr << "TileXRDirectCcuTrace customChannel.return" + << " devicePhyId=" << devicePhyId + << " op=" << opcode + << " dieId=" << static_cast(dieId) + << " driverRet=" << driverRet + << " opRet=" << out.opRet + << " offsetNext=" << out.offsetNextIdx + << "\n"; + TraceWords("customChannel.response", &out, std::min(sizeof(out), 256U)); + TraceWords("customChannel.responseTrailer", &out.offsetNextIdx, sizeof(out.offsetNextIdx) + sizeof(out.opRet)); +} + +} // namespace + +int TileXRCcuDriverAdapter::Init( + uint32_t devicePhyId, + TileXRCcuCustomChannelFn customChannel, + void* userData, + TileXRCcuDriverAdapterReport* report) +{ + ResetReport(report); + if (customChannel == nullptr) { + initialized_ = false; + return Fail(report, "missing CCU custom channel callback"); + } + devicePhyId_ = devicePhyId; + customChannel_ = customChannel; + userData_ = userData; + initialized_ = true; + FillCallReport(devicePhyId_, 0, 0, 0, 0, "ok", report); + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::Call( + uint8_t dieId, + uint32_t opcode, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const +{ + TileXRCcuCustomChannelIn in; + InitRequest(dieId, opcode, &in); + return CallPrepared(dieId, opcode, in, out, report); +} + +int TileXRCcuDriverAdapter::CallPrepared( + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (!initialized_ || customChannel_ == nullptr) { + return Fail(report, "CCU driver adapter is not initialized"); + } + if (out == nullptr) { + return Fail(report, "missing CCU custom channel output"); + } + + std::memset(out, 0, sizeof(*out)); + TraceCustomChannelRequest(devicePhyId_, dieId, opcode, in); + const int driverRet = customChannel_(devicePhyId_, in, out, userData_); + TraceCustomChannelReturn(devicePhyId_, dieId, opcode, driverRet, *out); + FillCallReport(devicePhyId_, dieId, opcode, driverRet, out->opRet, "ok", report); + if (driverRet != 0) { + return Fail( + report, + CcuCustomChannelFailureMessage("CCU custom channel call failed", opcode, driverRet, out->opRet), + TILEXR_ERROR_MKIRT); + } + if (out->opRet != 0) { + return Fail( + report, + CcuCustomChannelFailureMessage("CCU custom channel operation failed", opcode, driverRet, out->opRet), + TILEXR_ERROR_MKIRT); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::GetBasicInfo( + uint8_t dieId, + TileXRCcuBasicInfo* basicInfo, + TileXRCcuDriverAdapterReport* report) const +{ + if (basicInfo == nullptr) { + ResetReport(report); + return Fail(report, "missing output CCU basic info"); + } + *basicInfo = TileXRCcuBasicInfo{}; + + TileXRCcuCustomChannelOut out; + const int ret = Call(dieId, TILEXR_CCU_U_OP_GET_BASIC_INFO, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + const auto& raw = out.data.dataInfo.dataArray[0].baseinfo; + basicInfo->dieId = dieId; + basicInfo->msId = raw.msId; + basicInfo->msidToken.tokenId = raw.tokenId; + basicInfo->msidToken.tokenValue = raw.tokenValue; + basicInfo->msidToken.valid = raw.tokenValid != 0; + basicInfo->missionKey = raw.missionKey; + basicInfo->resourceAddr = raw.resourceAddr; + basicInfo->caps.cap0 = raw.caps.cap0; + basicInfo->caps.cap1 = raw.caps.cap1; + basicInfo->caps.cap2 = raw.caps.cap2; + basicInfo->caps.cap3 = raw.caps.cap3; + basicInfo->caps.cap4 = raw.caps.cap4; + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::GetDieEnabled( + uint8_t dieId, + bool* enabled, + TileXRCcuDriverAdapterReport* report) const +{ + if (enabled == nullptr) { + ResetReport(report); + return Fail(report, "missing output CCU die enabled flag"); + } + *enabled = false; + + TileXRCcuCustomChannelOut out; + const int ret = Call(dieId, TILEXR_CCU_U_OP_GET_DIE_WORKING, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + + *enabled = out.data.dataInfo.dataArray[0].dieinfo.enableFlag == TILEXR_CCU_ENABLE_FLAG; + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::ReadInstructions( + uint8_t dieId, + uint16_t instructionStartId, + void* instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (instructions == nullptr) { + return Fail(report, "missing output CCU instruction readback buffer"); + } + if (instructionCount == 0 || instructionCount > TILEXR_CCU_MAX_DATA_ARRAY_SIZE) { + return Fail(report, "invalid CCU instruction readback count"); + } + const uint32_t expectedBytes = instructionCount * TILEXR_CCU_INSTRUCTION_BYTES; + if (instructionBytes != expectedBytes) { + return Fail(report, "CCU instruction readback byte size mismatch"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_GET_INSTRUCTION, &in); + in.offsetStartIdx = instructionStartId; + in.data.dataInfo.dataArraySize = instructionCount; + in.data.dataInfo.dataLen = instructionBytes; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_GET_INSTRUCTION, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + auto* dst = static_cast(instructions); + for (uint32_t i = 0; i < instructionCount; ++i) { + std::memcpy( + dst + i * TILEXR_CCU_INSTRUCTION_BYTES, + out.data.dataInfo.dataArray[i].byte32.raw, + TILEXR_CCU_INSTRUCTION_BYTES); + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::InstallInstructions( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const +{ + return InstallInstructionsWithDataLen( + dieId, + instructionStartId, + instructionCount, + deviceInstructionAddr, + instructionBytes, + instructionBytes, + report); +} + +int TileXRCcuDriverAdapter::InstallInstructionsWithDataLen( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + uint32_t customChannelDataLen, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (instructionCount == 0) { + return Fail(report, "missing CCU instruction image"); + } + if (deviceInstructionAddr == 0) { + return Fail(report, "missing device CCU instruction image address"); + } + const uint32_t expectedBytes = static_cast(instructionCount) * TILEXR_CCU_INSTRUCTION_BYTES; + if (instructionBytes == 0 || instructionBytes != expectedBytes) { + return Fail(report, "CCU instruction image byte size mismatch"); + } + if (customChannelDataLen == 0) { + return Fail(report, "missing CCU instruction custom channel data length"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_INSTRUCTION, &in); + in.offsetStartIdx = instructionStartId; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = customChannelDataLen; + in.data.dataInfo.dataArray[0].insinfo.resourceAddr = deviceInstructionAddr; + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_INSTRUCTION, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallMsidToken( + uint8_t dieId, + uint32_t msId, + uint32_t tokenId, + uint32_t tokenValue, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_MSID_TOKEN, &in); + in.offsetStartIdx = 0; + in.data.dataInfo.dataArray[0].baseinfo.msId = msId; + in.data.dataInfo.dataArray[0].baseinfo.tokenId = tokenId; + in.data.dataInfo.dataArray[0].baseinfo.tokenValue = tokenValue; + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_MSID_TOKEN, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallPfeCtx( + uint8_t dieId, + uint32_t pfeOffset, + const TileXRCcuPfeCtx& ctx, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_PFE, &in); + in.offsetStartIdx = pfeOffset; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = TILEXR_CCU_PFE_CTX_BYTES; + std::memcpy(&in.data.dataInfo.dataArray[0], ctx.raw, TILEXR_CCU_PFE_CTX_BYTES); + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_PFE, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallJettyCtx( + uint8_t dieId, + uint16_t startJettyCtxId, + const TileXRCcuLocalJettyCtxData* ctxs, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (ctxs == nullptr) { + return Fail(report, "missing CCU local jetty context payloads"); + } + if (count == 0 || count > TILEXR_CCU_MAX_DATA_ARRAY_SIZE) { + return Fail(report, "invalid CCU local jetty context count"); + } + + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_JETTY_CTX, &in); + in.offsetStartIdx = startJettyCtxId; + in.data.dataInfo.dataArraySize = count; + in.data.dataInfo.dataLen = count * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES; + for (uint32_t i = 0; i < count; ++i) { + CopyPayloadToSlot(ctxs[i], &in.data.dataInfo.dataArray[i]); + } + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_JETTY_CTX, in, &out, report); +} + +int TileXRCcuDriverAdapter::InstallChannelCtxV1( + uint8_t dieId, + uint32_t channelId, + const TileXRCcuChannelCtxDataV1& ctx, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_CHANNEL, &in); + in.offsetStartIdx = channelId; + in.data.dataInfo.dataArraySize = 1; + in.data.dataInfo.dataLen = TILEXR_CCU_CHANNEL_CTX_V1_BYTES; + std::memcpy(&in.data.dataInfo.dataArray[0], ctx.raw, TILEXR_CCU_CHANNEL_CTX_V1_BYTES); + + TileXRCcuCustomChannelOut out; + return CallPrepared(dieId, TILEXR_CCU_U_OP_SET_CHANNEL, in, &out, report); +} + +int TileXRCcuDriverAdapter::ClearCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (count == 0) { + return Fail(report, "missing CCU CKE range"); + } + + uint32_t remaining = count; + uint32_t offset = startCkeId; + while (remaining > 0) { + const uint32_t batch = std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_CKE, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * TILEXR_CCU_CKE_SLOT_BYTES; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_SET_CKE, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + remaining -= batch; + offset += batch; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuDriverAdapter::InstallXnRange( + uint8_t dieId, + uint32_t startXnId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const +{ + ResetReport(report); + if (count == 0) { + return Fail(report, "missing CCU XN range"); + } + + uint32_t remaining = count; + uint32_t offset = startXnId; + while (remaining > 0) { + const uint32_t batch = std::min(remaining, TILEXR_CCU_MAX_DATA_ARRAY_SIZE); + TileXRCcuCustomChannelIn in; + InitRequest(dieId, TILEXR_CCU_U_OP_SET_XN, &in); + in.offsetStartIdx = offset; + in.data.dataInfo.dataArraySize = batch; + in.data.dataInfo.dataLen = batch * TILEXR_CCU_XN_SLOT_BYTES; + + TileXRCcuCustomChannelOut out; + const int ret = CallPrepared(dieId, TILEXR_CCU_U_OP_SET_XN, in, &out, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + remaining -= batch; + offset += batch; + } + return TILEXR_SUCCESS; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_driver_adapter.h b/src/comm/ccu/tilexr_ccu_driver_adapter.h new file mode 100644 index 00000000..65534113 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_driver_adapter.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_DRIVER_ADAPTER_H +#define TILEXR_CCU_DRIVER_ADAPTER_H + +#include "ccu/tilexr_ccu_hccp_types.h" +#include "ccu/tilexr_ccu_specs.h" + +#include +#include + +namespace TileXR { + +constexpr uint32_t TILEXR_CCU_U_OP_GET_BASIC_INFO = 11; +constexpr uint32_t TILEXR_CCU_U_OP_GET_DIE_WORKING = 15; +constexpr uint32_t TILEXR_CCU_U_OP_GET_INSTRUCTION = 201; +constexpr uint32_t TILEXR_CCU_U_OP_SET_MSID_TOKEN = 53; +constexpr uint32_t TILEXR_CCU_U_OP_SET_INSTRUCTION = 251; +constexpr uint32_t TILEXR_CCU_U_OP_SET_XN = 253; +constexpr uint32_t TILEXR_CCU_U_OP_SET_CKE = 254; +constexpr uint32_t TILEXR_CCU_U_OP_SET_PFE = 255; +constexpr uint32_t TILEXR_CCU_U_OP_SET_CHANNEL = 256; +constexpr uint32_t TILEXR_CCU_U_OP_SET_JETTY_CTX = 257; +constexpr uint32_t TILEXR_CCU_ENABLE_FLAG = 1; +constexpr uint32_t TILEXR_CCU_INSTRUCTION_BYTES = 32; +constexpr uint32_t TILEXR_CCU_DATA_ARRAY_SLOT_BYTES = 64; +constexpr uint32_t TILEXR_CCU_XN_SLOT_BYTES = 8; +constexpr uint32_t TILEXR_CCU_CKE_SLOT_BYTES = 8; +constexpr uint32_t TILEXR_CCU_PFE_CTX_BYTES = 8; +constexpr uint32_t TILEXR_CCU_LOCAL_JETTY_CTX_BYTES = 32; +constexpr uint32_t TILEXR_CCU_CHANNEL_CTX_V1_BYTES = 64; +constexpr uint32_t TILEXR_CCU_MAX_DATA_ARRAY_SIZE = 8; + +struct TileXRCcuPfeCtx { + uint8_t raw[TILEXR_CCU_PFE_CTX_BYTES]; +}; + +struct TileXRCcuLocalJettyCtxData { + uint8_t raw[TILEXR_CCU_LOCAL_JETTY_CTX_BYTES]; +}; + +struct TileXRCcuChannelCtxDataV1 { + uint8_t raw[TILEXR_CCU_CHANNEL_CTX_V1_BYTES]; +}; + +struct TileXRCcuDriverAdapterReport { + uint32_t devicePhyId = 0; + uint8_t dieId = 0; + uint32_t opcode = 0; + int driverRet = 0; + int opRet = 0; + std::string message; +}; + +using TileXRCcuCustomChannelFn = int (*)( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData); + +class TileXRCcuDriverAdapter { +public: + int Init( + uint32_t devicePhyId, + TileXRCcuCustomChannelFn customChannel, + void* userData, + TileXRCcuDriverAdapterReport* report); + + int GetBasicInfo(uint8_t dieId, TileXRCcuBasicInfo* basicInfo, TileXRCcuDriverAdapterReport* report) const; + int GetDieEnabled(uint8_t dieId, bool* enabled, TileXRCcuDriverAdapterReport* report) const; + int ReadInstructions( + uint8_t dieId, + uint16_t instructionStartId, + void* instructions, + uint32_t instructionCount, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const; + int InstallInstructions( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + TileXRCcuDriverAdapterReport* report) const; + int InstallInstructionsWithDataLen( + uint8_t dieId, + uint16_t instructionStartId, + uint16_t instructionCount, + uint64_t deviceInstructionAddr, + uint32_t instructionBytes, + uint32_t customChannelDataLen, + TileXRCcuDriverAdapterReport* report) const; + int InstallMsidToken( + uint8_t dieId, + uint32_t msId, + uint32_t tokenId, + uint32_t tokenValue, + TileXRCcuDriverAdapterReport* report) const; + int InstallPfeCtx( + uint8_t dieId, + uint32_t pfeOffset, + const TileXRCcuPfeCtx& ctx, + TileXRCcuDriverAdapterReport* report) const; + int InstallJettyCtx( + uint8_t dieId, + uint16_t startJettyCtxId, + const TileXRCcuLocalJettyCtxData* ctxs, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + int InstallChannelCtxV1( + uint8_t dieId, + uint32_t channelId, + const TileXRCcuChannelCtxDataV1& ctx, + TileXRCcuDriverAdapterReport* report) const; + int ClearCkeRange( + uint8_t dieId, + uint32_t startCkeId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + int InstallXnRange( + uint8_t dieId, + uint32_t startXnId, + uint32_t count, + TileXRCcuDriverAdapterReport* report) const; + +private: + int Call(uint8_t dieId, uint32_t opcode, TileXRCcuCustomChannelOut* out, TileXRCcuDriverAdapterReport* report) + const; + int CallPrepared( + uint8_t dieId, + uint32_t opcode, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + TileXRCcuDriverAdapterReport* report) const; + + uint32_t devicePhyId_ = 0; + TileXRCcuCustomChannelFn customChannel_ = nullptr; + void* userData_ = nullptr; + bool initialized_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_DRIVER_ADAPTER_H diff --git a/src/comm/ccu/tilexr_ccu_hccp_loader.cpp b/src/comm/ccu/tilexr_ccu_hccp_loader.cpp new file mode 100644 index 00000000..3f28935a --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_hccp_loader.h" + +#include "tilexr_types.h" + +#include +#include +#include +#include +#include +#include + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuHccpLoaderReport{}; + } +} + +int Fail(TileXRCcuHccpLoaderReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_NOT_FOUND; +} + +int FailWithCode(TileXRCcuHccpLoaderReport* report, const std::string& message, int code) +{ + if (report != nullptr) { + report->message = message; + } + return code; +} + +template +bool LoadSymbol(void* handle, Fn& out, const char* primary, const char* fallback) +{ + out = reinterpret_cast(dlsym(handle, primary)); + if (out == nullptr && fallback != nullptr) { + out = reinterpret_cast(dlsym(handle, fallback)); + } + return out != nullptr; +} + +template +void LoadOptionalSymbol(void* handle, Fn& out, const char* primary, const char* fallback) +{ + if (handle == nullptr) { + out = nullptr; + return; + } + out = reinterpret_cast(dlsym(handle, primary)); + if (out == nullptr && fallback != nullptr) { + out = reinterpret_cast(dlsym(handle, fallback)); + } +} + +using RaHdcKey = std::pair; + +std::mutex g_raHdcMtx; +std::map g_raHdcRefs; +uint32_t g_netServiceRefs = 0; +int g_netServiceHdcType = 0; + +struct CcuTlvSession { + void* handle = nullptr; + uint32_t bufferSize = 0; + uint32_t refs = 0; +}; + +std::mutex g_ccuTlvMtx; +std::map g_ccuTlvSessions; + +bool EnvFlag(const char* name) +{ + const char* value = std::getenv(name); + return value != nullptr && value[0] != '\0' && value[0] != '0'; +} + +std::string RaConfigText(const TileXRCcuRaInitConfig& config) +{ + std::ostringstream text; + text << "phyId=" << config.phyId + << " nicPosition=" << config.nicPosition + << " hdcType=" << config.hdcType + << " enableHdcAsync=" << (config.enableHdcAsync ? 1 : 0); + return text.str(); +} + +} // namespace + +TileXRCcuHccpLoader::~TileXRCcuHccpLoader() +{ + Unload(); +} + +int TileXRCcuHccpLoader::Load(TileXRCcuHccpLoaderReport* report) +{ + ResetReport(report); + if (loaded_) { + if (report != nullptr) { + report->loaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + raHandle_ = dlopen("libra.so", RTLD_NOW); + if (raHandle_ == nullptr) { + return Fail(report, std::string("failed to load libra.so: ") + dlerror()); + } + + if (!LoadSymbol(raHandle_, RaCustomChannel, "RaCustomChannel", "ra_custom_channel")) { + Unload(); + return Fail(report, "missing RaCustomChannel/ra_custom_channel in libra.so"); + } + if (!LoadSymbol(raHandle_, RaInit, "RaInit", nullptr)) { + Unload(); + return Fail(report, "missing RaInit in libra.so"); + } + if (!LoadSymbol(raHandle_, RaDeinit, "RaDeinit", nullptr)) { + Unload(); + return Fail(report, "missing RaDeinit in libra.so"); + } + LoadOptionalSymbol(raHandle_, RaTlvInit, "RaTlvInit", nullptr); + LoadOptionalSymbol(raHandle_, RaTlvRequest, "RaTlvRequest", nullptr); + LoadOptionalSymbol(raHandle_, RaTlvDeinit, "RaTlvDeinit", nullptr); + LoadOptionalSymbol(raHandle_, RaGetDevEidInfoNum, "RaGetDevEidInfoNum", "ra_get_dev_eid_info_num"); + LoadOptionalSymbol(raHandle_, RaGetDevEidInfoList, "RaGetDevEidInfoList", "ra_get_dev_eid_info_list"); + LoadOptionalSymbol(raHandle_, RaCtxInit, "RaCtxInit", "ra_ctx_init"); + LoadOptionalSymbol(raHandle_, RaCtxDeinit, "RaCtxDeinit", "ra_ctx_deinit"); + LoadOptionalSymbol(raHandle_, RaCtxTokenIdAlloc, "RaCtxTokenIdAlloc", "ra_ctx_token_id_alloc"); + LoadOptionalSymbol(raHandle_, RaCtxTokenIdFree, "RaCtxTokenIdFree", "ra_ctx_token_id_free"); + LoadOptionalSymbol(raHandle_, RaCtxLmemRegister, "RaCtxLmemRegister", "ra_ctx_lmem_register"); + LoadOptionalSymbol(raHandle_, RaCtxLmemUnregister, "RaCtxLmemUnregister", "ra_ctx_lmem_unregister"); + LoadOptionalSymbol(raHandle_, RaGetSecRandom, "RaGetSecRandom", "ra_get_sec_random"); + LoadOptionalSymbol(raHandle_, RaCtxChanCreate, "RaCtxChanCreate", "ra_ctx_chan_create"); + LoadOptionalSymbol(raHandle_, RaCtxChanDestroy, "RaCtxChanDestroy", "ra_ctx_chan_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxCqCreate, "RaCtxCqCreate", "ra_ctx_cq_create"); + LoadOptionalSymbol(raHandle_, RaCtxCqDestroy, "RaCtxCqDestroy", "ra_ctx_cq_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxQpCreate, "RaCtxQpCreate", "ra_ctx_qp_create"); + LoadOptionalSymbol(raHandle_, RaCtxQpDestroy, "RaCtxQpDestroy", "ra_ctx_qp_destroy"); + LoadOptionalSymbol(raHandle_, RaCtxQpImport, "RaCtxQpImport", "ra_ctx_qp_import"); + LoadOptionalSymbol(raHandle_, RaCtxQpUnimport, "RaCtxQpUnimport", "ra_ctx_qp_unimport"); + LoadOptionalSymbol(raHandle_, RaCtxQpBind, "RaCtxQpBind", "ra_ctx_qp_bind"); + LoadOptionalSymbol(raHandle_, RaCtxQpUnbind, "RaCtxQpUnbind", "ra_ctx_qp_unbind"); + LoadOptionalSymbol(raHandle_, RaGetTpInfoListAsync, "RaGetTpInfoListAsync", "ra_get_tp_info_list_async"); + LoadOptionalSymbol(raHandle_, RaGetAsyncReqResult, "RaGetAsyncReqResult", "ra_get_async_req_result"); + + runtimeHandle_ = dlopen("libruntime.so", RTLD_NOW); + LoadOptionalSymbol(runtimeHandle_, RtGetDevicePhyIdByIndex, "rtGetDevicePhyIdByIndex", nullptr); + LoadOptionalSymbol(runtimeHandle_, RtOpenNetService, "rtOpenNetService", nullptr); + LoadOptionalSymbol(runtimeHandle_, RtCloseNetService, "rtCloseNetService", nullptr); + + loaded_ = true; + if (report != nullptr) { + report->loaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::LoadEndpointRouteProviderFromEnv(TileXRCcuHccpLoaderReport* report) +{ + ResetReport(report); + if (CollectLocalEndpointRoute != nullptr) { + if (report != nullptr) { + report->endpointRouteProviderLoaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + const char* providerPath = std::getenv("TILEXR_CCU_ENDPOINT_ROUTE_PROVIDER"); + if (providerPath == nullptr || providerPath[0] == '\0') { + return Fail(report, "direct CCU endpoint route provider is not configured"); + } + if (report != nullptr) { + report->endpointRouteProviderConfigured = true; + } + + endpointRouteProviderHandle_ = dlopen(providerPath, RTLD_NOW); + if (endpointRouteProviderHandle_ == nullptr) { + return Fail(report, std::string("failed to load direct CCU endpoint route provider: ") + dlerror()); + } + + if (!LoadSymbol( + endpointRouteProviderHandle_, + CollectLocalEndpointRoute, + "TileXRCcuCollectLocalEndpointRoute", + "tilexr_ccu_collect_local_endpoint_route")) { + dlclose(endpointRouteProviderHandle_); + endpointRouteProviderHandle_ = nullptr; + return Fail(report, "missing TileXRCcuCollectLocalEndpointRoute in direct CCU endpoint route provider"); + } + + if (report != nullptr) { + report->endpointRouteProviderLoaded = true; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +void TileXRCcuHccpLoader::Unload() +{ + ReleaseCcuTlv(); + ReleaseRaHdc(); + RaCustomChannel = nullptr; + RtGetDevicePhyIdByIndex = nullptr; + RtOpenNetService = nullptr; + RtCloseNetService = nullptr; + RaInit = nullptr; + RaDeinit = nullptr; + RaTlvInit = nullptr; + RaTlvRequest = nullptr; + RaTlvDeinit = nullptr; + RaGetDevEidInfoNum = nullptr; + RaGetDevEidInfoList = nullptr; + RaCtxInit = nullptr; + RaCtxDeinit = nullptr; + RaCtxTokenIdAlloc = nullptr; + RaCtxTokenIdFree = nullptr; + RaCtxLmemRegister = nullptr; + RaCtxLmemUnregister = nullptr; + RaGetSecRandom = nullptr; + RaCtxChanCreate = nullptr; + RaCtxChanDestroy = nullptr; + RaCtxCqCreate = nullptr; + RaCtxCqDestroy = nullptr; + RaCtxQpCreate = nullptr; + RaCtxQpDestroy = nullptr; + RaCtxQpImport = nullptr; + RaCtxQpUnimport = nullptr; + RaCtxQpBind = nullptr; + RaCtxQpUnbind = nullptr; + RaGetTpInfoListAsync = nullptr; + RaGetAsyncReqResult = nullptr; + CollectLocalEndpointRoute = nullptr; + loaded_ = false; + if (endpointRouteProviderHandle_ != nullptr) { + dlclose(endpointRouteProviderHandle_); + endpointRouteProviderHandle_ = nullptr; + } + if (runtimeHandle_ != nullptr) { + dlclose(runtimeHandle_); + runtimeHandle_ = nullptr; + } + if (raHandle_ != nullptr) { + dlclose(raHandle_); + raHandle_ = nullptr; + } +} + +bool TileXRCcuHccpLoader::IsLoaded() const +{ + return loaded_; +} + +int TileXRCcuHccpLoader::ResolveDevicePhyId( + uint32_t logicDevId, + uint32_t* phyId, + TileXRCcuHccpLoaderReport* report) const +{ + if (report != nullptr) { + report->logicDevId = logicDevId; + report->runtimePhyIdRet = 0; + } + if (phyId == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!loaded_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + if (RtGetDevicePhyIdByIndex == nullptr) { + if (EnvFlag("TILEXR_CCU_DIRECT_ALLOW_LOGIC_PHY_FALLBACK")) { + *phyId = logicDevId; + if (report != nullptr) { + report->devicePhyId = *phyId; + report->message = "rtGetDevicePhyIdByIndex missing, using logic device id fallback"; + } + return TILEXR_SUCCESS; + } + return FailWithCode(report, "missing rtGetDevicePhyIdByIndex in libruntime.so", TILEXR_ERROR_NOT_FOUND); + } + uint32_t resolvedPhyId = 0; + const int ret = RtGetDevicePhyIdByIndex(logicDevId, &resolvedPhyId); + if (report != nullptr) { + report->runtimePhyIdRet = ret; + } + if (ret != 0) { + return FailWithCode(report, "rtGetDevicePhyIdByIndex failed", TILEXR_ERROR_MKIRT); + } + *phyId = resolvedPhyId; + if (report != nullptr) { + report->devicePhyId = *phyId; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::InitRaHdc( + uint32_t devicePhyId, + int hdcType, + bool enableHdcAsync, + TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + report->devicePhyId = devicePhyId; + report->hdcType = hdcType; + } + if (!loaded_) { + return FailWithCode(report, "CCU HCCP loader is not initialized for RA init", TILEXR_ERROR_NOT_INITIALIZED); + } + if (RaInit == nullptr || RaDeinit == nullptr) { + return FailWithCode(report, "missing RaInit/RaDeinit in libra.so", TILEXR_ERROR_NOT_FOUND); + } + if (raHdcInitialized_) { + std::lock_guard lock(g_raHdcMtx); + const auto it = g_raHdcRefs.find(RaHdcKey(raInitConfig_.phyId, raInitConfig_.hdcType)); + if (report != nullptr) { + report->raInitialized = true; + report->raInitRefCount = it == g_raHdcRefs.end() ? 0U : it->second; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + TileXRCcuRaInitConfig config {}; + config.phyId = devicePhyId; + config.nicPosition = TILEXR_CCU_NETWORK_OFFLINE; + config.hdcType = hdcType; + config.enableHdcAsync = enableHdcAsync; + + const RaHdcKey key(config.phyId, config.hdcType); + std::lock_guard lock(g_raHdcMtx); + int ret = AcquireNetServiceLocked(hdcType, report); + if (ret != TILEXR_SUCCESS) { + return ret; + } + uint32_t& refCount = g_raHdcRefs[key]; + if (refCount == 0) { + ret = RaInit(&config); + if (report != nullptr) { + report->raInitRet = ret; + } + if (ret != 0) { + g_raHdcRefs.erase(key); + ReleaseNetServiceLocked(nullptr); + std::ostringstream message; + message << "RaInit failed ret=" << ret << ": " << RaConfigText(config); + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + } + ++refCount; + raInitConfig_ = config; + raHdcInitialized_ = true; + if (report != nullptr) { + report->raInitialized = true; + report->raInitRefCount = refCount; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::InitCcuTlv(uint32_t devicePhyId, TileXRCcuHccpLoaderReport* report) +{ + if (report != nullptr) { + report->devicePhyId = devicePhyId; + } + if (!loaded_) { + return FailWithCode(report, "CCU HCCP loader is not initialized for TLV init", + TILEXR_ERROR_NOT_INITIALIZED); + } + if (RaTlvInit == nullptr || RaTlvRequest == nullptr || RaTlvDeinit == nullptr) { + return FailWithCode(report, "missing RaTlvInit/RaTlvRequest/RaTlvDeinit in libra.so", + TILEXR_ERROR_NOT_FOUND); + } + if (ccuTlvInitialized_) { + std::lock_guard lock(g_ccuTlvMtx); + const auto it = g_ccuTlvSessions.find(ccuTlvDevicePhyId_); + if (report != nullptr) { + report->ccuTlvInitialized = true; + report->ccuTlvRefCount = it == g_ccuTlvSessions.end() ? 0U : it->second.refs; + report->ccuTlvBufferSize = it == g_ccuTlvSessions.end() ? 0U : it->second.bufferSize; + report->message = "ok"; + } + return TILEXR_SUCCESS; + } + + std::lock_guard lock(g_ccuTlvMtx); + CcuTlvSession& session = g_ccuTlvSessions[devicePhyId]; + if (session.refs == 0) { + TileXRCcuTlvInitInfo initInfo {}; + initInfo.version = TILEXR_CCU_TLV_VERSION; + initInfo.phyId = devicePhyId; + initInfo.nicPosition = TILEXR_CCU_NETWORK_OFFLINE; + uint32_t bufferSize = 0; + void* tlvHandle = nullptr; + int ret = RaTlvInit(&initInfo, &bufferSize, &tlvHandle); + if (report != nullptr) { + report->raTlvInitRet = ret; + report->ccuTlvBufferSize = bufferSize; + } + if (ret != 0 || tlvHandle == nullptr) { + g_ccuTlvSessions.erase(devicePhyId); + std::ostringstream message; + message << "RaTlvInit failed ret=" << ret + << ": phyId=" << initInfo.phyId + << " nicPosition=" << initInfo.nicPosition + << " version=" << initInfo.version; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + + TileXRCcuTlvMsg sendMsg {}; + TileXRCcuTlvMsg recvMsg {}; + sendMsg.type = TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT; + ret = RaTlvRequest(tlvHandle, TILEXR_CCU_TLV_MODULE_TYPE_CCU, &sendMsg, &recvMsg); + if (report != nullptr) { + report->raTlvRequestRet = ret; + } + if (ret != 0) { + (void)RaTlvDeinit(tlvHandle); + g_ccuTlvSessions.erase(devicePhyId); + std::ostringstream message; + message << "RaTlvRequest CCU_INIT failed ret=" << ret + << ": phyId=" << devicePhyId + << " moduleType=" << TILEXR_CCU_TLV_MODULE_TYPE_CCU + << " msgType=" << TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + session.handle = tlvHandle; + session.bufferSize = bufferSize; + } + + ++session.refs; + ccuTlvDevicePhyId_ = devicePhyId; + ccuTlvInitialized_ = true; + if (report != nullptr) { + report->ccuTlvInitialized = true; + report->ccuTlvRefCount = session.refs; + report->ccuTlvBufferSize = session.bufferSize; + report->message = "ok"; + } + return TILEXR_SUCCESS; +} + +int TileXRCcuHccpLoader::AcquireNetServiceLocked(int hdcType, TileXRCcuHccpLoaderReport* report) +{ + if (RtOpenNetService == nullptr || RtCloseNetService == nullptr) { + return FailWithCode(report, + "missing rtOpenNetService/rtCloseNetService in libruntime.so", + TILEXR_ERROR_NOT_FOUND); + } + if (g_netServiceRefs > 0) { + if (g_netServiceHdcType != hdcType) { + std::ostringstream message; + message << "runtime net service already opened for hdcType=" << g_netServiceHdcType + << ", requested hdcType=" << hdcType; + return FailWithCode(report, message.str(), TILEXR_ERROR_PARA_CHECK_FAIL); + } + ++g_netServiceRefs; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return TILEXR_SUCCESS; + } + + std::string extParamText("--hdcType=" + std::to_string(hdcType)); + TileXRCcuRtProcExtParam extParam {}; + extParam.paramInfo = extParamText.c_str(); + extParam.paramLen = extParamText.size(); + TileXRCcuRtNetServiceOpenArgs openArgs {}; + openArgs.extParamList = &extParam; + openArgs.extParamCnt = 1; + const int ret = RtOpenNetService(&openArgs); + if (report != nullptr) { + report->rtOpenNetServiceRet = ret; + } + if (ret != 0) { + std::ostringstream message; + message << "rtOpenNetService failed ret=" << ret << ": " << extParamText; + return FailWithCode(report, message.str(), TILEXR_ERROR_MKIRT); + } + + g_netServiceHdcType = hdcType; + g_netServiceRefs = 1; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return TILEXR_SUCCESS; +} + +void TileXRCcuHccpLoader::ReleaseNetServiceLocked(TileXRCcuHccpLoaderReport* report) +{ + if (g_netServiceRefs == 0) { + return; + } + if (g_netServiceRefs > 1U) { + --g_netServiceRefs; + if (report != nullptr) { + report->netServiceRefCount = g_netServiceRefs; + } + return; + } + + int ret = 0; + if (RtCloseNetService != nullptr) { + ret = RtCloseNetService(); + } + if (report != nullptr) { + report->rtCloseNetServiceRet = ret; + report->netServiceRefCount = 0; + } + g_netServiceRefs = 0; + g_netServiceHdcType = 0; +} + +void TileXRCcuHccpLoader::ReleaseCcuTlv() +{ + if (!ccuTlvInitialized_) { + return; + } + std::lock_guard lock(g_ccuTlvMtx); + auto it = g_ccuTlvSessions.find(ccuTlvDevicePhyId_); + if (it != g_ccuTlvSessions.end() && it->second.refs > 1U) { + --it->second.refs; + ccuTlvInitialized_ = false; + ccuTlvDevicePhyId_ = 0; + return; + } + if (it != g_ccuTlvSessions.end()) { + if (RaTlvRequest != nullptr && it->second.handle != nullptr) { + TileXRCcuTlvMsg sendMsg {}; + TileXRCcuTlvMsg recvMsg {}; + sendMsg.type = TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT; + (void)RaTlvRequest(it->second.handle, TILEXR_CCU_TLV_MODULE_TYPE_CCU, &sendMsg, &recvMsg); + } + if (RaTlvDeinit != nullptr && it->second.handle != nullptr) { + (void)RaTlvDeinit(it->second.handle); + } + g_ccuTlvSessions.erase(it); + } + ccuTlvInitialized_ = false; + ccuTlvDevicePhyId_ = 0; +} + +void TileXRCcuHccpLoader::ReleaseRaHdc() +{ + if (!raHdcInitialized_) { + return; + } + const RaHdcKey key(raInitConfig_.phyId, raInitConfig_.hdcType); + std::lock_guard lock(g_raHdcMtx); + auto it = g_raHdcRefs.find(key); + if (it != g_raHdcRefs.end() && it->second > 1U) { + --it->second; + ReleaseNetServiceLocked(nullptr); + raHdcInitialized_ = false; + raInitConfig_ = TileXRCcuRaInitConfig {}; + return; + } + if (RaDeinit != nullptr) { + (void)RaDeinit(&raInitConfig_); + } + if (it != g_raHdcRefs.end()) { + g_raHdcRefs.erase(it); + } + ReleaseNetServiceLocked(nullptr); + raHdcInitialized_ = false; + raInitConfig_ = TileXRCcuRaInitConfig {}; +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_hccp_loader.h b/src/comm/ccu/tilexr_ccu_hccp_loader.h new file mode 100644 index 00000000..93ea1348 --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_loader.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_HCCP_LOADER_H +#define TILEXR_CCU_HCCP_LOADER_H + +#include "ccu/tilexr_ccu_hccp_types.h" +#include "tilexr_types.h" + +#include + +namespace TileXR { + +struct TileXRCcuHccpLoaderReport { + bool loaded = false; + bool endpointRouteProviderConfigured = false; + bool endpointRouteProviderLoaded = false; + bool raInitialized = false; + bool ccuTlvInitialized = false; + uint32_t logicDevId = 0; + uint32_t devicePhyId = 0; + uint32_t raInitRefCount = 0; + uint32_t netServiceRefCount = 0; + uint32_t ccuTlvRefCount = 0; + uint32_t ccuTlvBufferSize = 0; + int hdcType = 0; + int runtimePhyIdRet = 0; + int rtOpenNetServiceRet = 0; + int rtCloseNetServiceRet = 0; + int raInitRet = 0; + int raDeinitRet = 0; + int raTlvInitRet = 0; + int raTlvRequestRet = 0; + int raTlvDeinitRet = 0; + std::string message; +}; + +class TileXRCcuHccpLoader { +public: + TileXRCcuHccpLoader() = default; + ~TileXRCcuHccpLoader(); + TileXRCcuHccpLoader(const TileXRCcuHccpLoader&) = delete; + TileXRCcuHccpLoader& operator=(const TileXRCcuHccpLoader&) = delete; + + int Load(TileXRCcuHccpLoaderReport* report); + int LoadEndpointRouteProviderFromEnv(TileXRCcuHccpLoaderReport* report); + int InitRaHdc( + uint32_t devicePhyId, + int hdcType, + bool enableHdcAsync, + TileXRCcuHccpLoaderReport* report); + int InitCcuTlv(uint32_t devicePhyId, TileXRCcuHccpLoaderReport* report); + void Unload(); + bool IsLoaded() const; + int ResolveDevicePhyId(uint32_t logicDevId, uint32_t* phyId, TileXRCcuHccpLoaderReport* report = nullptr) const; + + TileXRCcuRaCustomChannelFunc RaCustomChannel = nullptr; + TileXRCcuRtGetDevicePhyIdByIndexFunc RtGetDevicePhyIdByIndex = nullptr; + TileXRCcuRtOpenNetServiceFunc RtOpenNetService = nullptr; + TileXRCcuRtCloseNetServiceFunc RtCloseNetService = nullptr; + TileXRCcuRaInitFunc RaInit = nullptr; + TileXRCcuRaDeinitFunc RaDeinit = nullptr; + TileXRCcuRaTlvInitFunc RaTlvInit = nullptr; + TileXRCcuRaTlvRequestFunc RaTlvRequest = nullptr; + TileXRCcuRaTlvDeinitFunc RaTlvDeinit = nullptr; + TileXRCcuRaGetDevEidInfoNumFunc RaGetDevEidInfoNum = nullptr; + TileXRCcuRaGetDevEidInfoListFunc RaGetDevEidInfoList = nullptr; + TileXRCcuRaCtxInitFunc RaCtxInit = nullptr; + TileXRCcuRaCtxDeinitFunc RaCtxDeinit = nullptr; + TileXRCcuRaCtxTokenIdAllocFunc RaCtxTokenIdAlloc = nullptr; + TileXRCcuRaCtxTokenIdFreeFunc RaCtxTokenIdFree = nullptr; + TileXRCcuRaCtxLmemRegisterFunc RaCtxLmemRegister = nullptr; + TileXRCcuRaCtxLmemUnregisterFunc RaCtxLmemUnregister = nullptr; + TileXRCcuRaGetSecRandomFunc RaGetSecRandom = nullptr; + TileXRCcuRaCtxChanCreateFunc RaCtxChanCreate = nullptr; + TileXRCcuRaCtxChanDestroyFunc RaCtxChanDestroy = nullptr; + TileXRCcuRaCtxCqCreateFunc RaCtxCqCreate = nullptr; + TileXRCcuRaCtxCqDestroyFunc RaCtxCqDestroy = nullptr; + TileXRCcuRaCtxQpCreateFunc RaCtxQpCreate = nullptr; + TileXRCcuRaCtxQpDestroyFunc RaCtxQpDestroy = nullptr; + TileXRCcuRaCtxQpImportFunc RaCtxQpImport = nullptr; + TileXRCcuRaCtxQpUnimportFunc RaCtxQpUnimport = nullptr; + TileXRCcuRaCtxQpBindFunc RaCtxQpBind = nullptr; + TileXRCcuRaCtxQpUnbindFunc RaCtxQpUnbind = nullptr; + TileXRCcuRaGetTpInfoListAsyncFunc RaGetTpInfoListAsync = nullptr; + TileXRCcuRaGetAsyncReqResultFunc RaGetAsyncReqResult = nullptr; + TileXRCcuEndpointRouteProviderFunc CollectLocalEndpointRoute = nullptr; + +private: + void ReleaseCcuTlv(); + void ReleaseRaHdc(); + int AcquireNetServiceLocked(int hdcType, TileXRCcuHccpLoaderReport* report); + void ReleaseNetServiceLocked(TileXRCcuHccpLoaderReport* report = nullptr); + + void* raHandle_ = nullptr; + void* runtimeHandle_ = nullptr; + void* endpointRouteProviderHandle_ = nullptr; + TileXRCcuRaInitConfig raInitConfig_ = {}; + uint32_t ccuTlvDevicePhyId_ = 0; + bool raHdcInitialized_ = false; + bool ccuTlvInitialized_ = false; + bool loaded_ = false; +}; + +} // namespace TileXR + +#endif // TILEXR_CCU_HCCP_LOADER_H diff --git a/src/comm/ccu/tilexr_ccu_hccp_types.h b/src/comm/ccu/tilexr_ccu_hccp_types.h new file mode 100644 index 00000000..abc75b1a --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_hccp_types.h @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#ifndef TILEXR_CCU_HCCP_TYPES_H +#define TILEXR_CCU_HCCP_TYPES_H + +#include "ccu/tilexr_ccu_abi_constants.h" + +#include + +namespace TileXR { + +constexpr int TILEXR_CCU_NETWORK_OFFLINE = 1; +constexpr int TILEXR_CCU_HDC_SERVICE_TYPE_RDMA = 6; +constexpr int TILEXR_CCU_HDC_SERVICE_TYPE_RDMA_V2 = 18; +constexpr uint32_t TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE = 2048; +constexpr int TILEXR_CCU_TLV_VERSION = 1; +constexpr uint32_t TILEXR_CCU_TLV_MODULE_TYPE_CCU = 1; +constexpr uint32_t TILEXR_CCU_TLV_MSG_TYPE_CCU_INIT = 0; +constexpr uint32_t TILEXR_CCU_TLV_MSG_TYPE_CCU_UNINIT = 1; +constexpr uint32_t TILEXR_CCU_HCCP_DEV_EID_INFO_NAME_BYTES = 64; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_KEY_BYTES = 128; +constexpr uint32_t TILEXR_CCU_HCCP_TOKEN_POLICY_PLAIN_TEXT = 1; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_READ = 1U << 1U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_WRITE = 1U << 2U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_ATOMIC = 1U << 3U; +constexpr uint32_t TILEXR_CCU_HCCP_MEM_SEG_ACCESS_DEFAULT = + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_READ | + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_WRITE | + TILEXR_CCU_HCCP_MEM_SEG_ACCESS_ATOMIC; +constexpr uint32_t TILEXR_CCU_HCCP_QP_KEY_BYTES = 64; +constexpr uint32_t TILEXR_CCU_HCCP_CQ_DEPTH_DEFAULT = 16384; +constexpr uint32_t TILEXR_CCU_HCCP_RQ_DEPTH_DEFAULT = 256; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_MODE_CCU = 2; +constexpr uint32_t TILEXR_CCU_HCCP_TRANSPORT_MODE_RM = 1; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_IMPORT_MODE_EXP = 1; +constexpr uint32_t TILEXR_CCU_HCCP_JETTY_GRP_POLICY_RR = 0; +constexpr uint32_t TILEXR_CCU_HCCP_TARGET_TYPE_JETTY = 1; +constexpr uint32_t TILEXR_CCU_HCCP_TP_TYPE_RTP = 0; +constexpr uint32_t TILEXR_CCU_HCCP_TP_TYPE_CTP = 1; +constexpr uint8_t TILEXR_CCU_HCCP_RNR_RETRY_DEFAULT = 7; + +struct TileXRCcuDataByte8 { + char raw[8]; +}; + +struct TileXRCcuDataByte32 { + char raw[32]; +}; + +struct TileXRCcuDataByte64 { + char raw[64]; +}; + +struct TileXRCcuCustomChannelCaps { + uint32_t cap0; + uint32_t cap1; + uint32_t cap2; + uint32_t cap3; + uint32_t cap4; +}; + +struct TileXRCcuInstrInfo { + uint64_t resourceAddr; +}; + +struct TileXRCcuDieInfo { + uint32_t enableFlag; +}; + +struct TileXRCcuBaseInfoData { + uint32_t msId; + uint32_t tokenId; + uint32_t tokenValue; + uint32_t tokenValid; + uint32_t missionKey; + uint64_t resourceAddr; + TileXRCcuCustomChannelCaps caps; +}; + +union TileXRCcuDataTypeUnion { + TileXRCcuDataByte8 byte8; + TileXRCcuDataByte32 byte32; + TileXRCcuDataByte64 byte64; + TileXRCcuBaseInfoData baseinfo; + TileXRCcuInstrInfo insinfo; + TileXRCcuDieInfo dieinfo; +}; + +struct TileXRCcuData { + uint32_t udieIdx; + uint32_t dataLen; + uint32_t dataArraySize; + TileXRCcuDataTypeUnion dataArray[8]; +}; + +union TileXRCcuDataUnion { + char raw[TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE]; + TileXRCcuData dataInfo; +}; + +struct TileXRCcuCustomChannelIn { + TileXRCcuDataUnion data; + uint32_t offsetStartIdx; + uint32_t op; +}; + +struct TileXRCcuCustomChannelOut { + TileXRCcuDataUnion data; + uint32_t offsetNextIdx; + int opRet; +}; + +struct TileXRCcuRaInfo { + int mode; + uint32_t phyId; +}; + +union TileXRCcuHccpEid { + uint8_t raw[TILEXR_CCU_EID_BYTES]; + struct { + uint64_t reserved; + uint32_t prefix; + uint32_t addr; + } in4; + struct { + uint64_t subnetPrefix; + uint64_t interfaceId; + } in6; +}; + +struct TileXRCcuHccpDevEidInfo { + char name[TILEXR_CCU_HCCP_DEV_EID_INFO_NAME_BYTES]; + uint32_t type; + uint32_t eidIndex; + TileXRCcuHccpEid eid; + uint32_t dieId; + uint32_t chipId; + uint32_t funcId; + uint32_t resv; +}; + +struct TileXRCcuHccpCtxInitCfg { + int mode; + union { + struct { + bool disabledLiteThread; + } rdma; + }; +}; + +struct TileXRCcuHccpCtxInitAttr { + uint32_t phyId; + union { + uint8_t rdmaPad[24]; + struct { + uint32_t eidIndex; + TileXRCcuHccpEid eid; + } ub; + }; + uint32_t resv[16]; +}; + +struct TileXRCcuHccpTokenId { + uint32_t tokenId; +}; + +struct TileXRCcuHccpMemKey { + uint8_t value[TILEXR_CCU_HCCP_MEM_KEY_BYTES]; + uint8_t size; +}; + +struct TileXRCcuHccpMemInfo { + uint64_t addr; + uint64_t size; +}; + +union TileXRCcuHccpRegSegFlag { + struct { + uint32_t tokenPolicy : 3; + uint32_t cacheable : 1; + uint32_t dsva : 1; + uint32_t access : 6; + uint32_t nonPin : 1; + uint32_t userIova : 1; + uint32_t tokenIdValid : 1; + uint32_t reserved : 18; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpMemRegAttr { + TileXRCcuHccpMemInfo mem; + union { + struct { + int access; + } rdma; + struct { + TileXRCcuHccpRegSegFlag flags; + uint32_t tokenValue; + void* tokenIdHandle; + } ub; + }; + uint32_t resv[8]; +}; + +struct TileXRCcuHccpMemRegInfo { + TileXRCcuHccpMemKey key; + union { + struct { + uint32_t lkey; + } rdma; + struct { + uint32_t tokenId; + uint64_t targetSegHandle; + } ub; + }; + uint32_t resv[8]; +}; + +struct TileXRCcuHccpMrRegInfo { + TileXRCcuHccpMemRegAttr in; + TileXRCcuHccpMemRegInfo out; +}; + +union TileXRCcuHccpDataPlaneCstmFlag { + struct { + uint32_t pollCqCstm : 1; + uint32_t reserved : 31; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpChanInfo { + struct { + TileXRCcuHccpDataPlaneCstmFlag dataPlaneFlag; + } in; + struct { + int fd; + } out; +}; + +union TileXRCcuHccpJfcFlag { + struct { + uint32_t lockFree : 1; + uint32_t jfcInline : 1; + uint32_t reserved : 30; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpCqInfo { + struct { + void* chanHandle; + uint32_t depth; + union { + struct { + uint64_t cqContext; + uint32_t mode; + uint32_t compVector; + } rdma; + struct { + uint64_t userCtx; + int mode; + uint32_t ceqn; + TileXRCcuHccpJfcFlag flag; + struct { + bool valid; + uint32_t cqeFlag; + } ccuExCfg; + } ub; + }; + } in; + struct { + uint64_t va; + uint32_t id; + uint32_t cqeSize; + uint64_t bufAddr; + uint64_t swdbAddr; + } out; +}; + +union TileXRCcuHccpJettyFlag { + struct { + uint32_t shareJfr : 1; + uint32_t reserved : 31; + } bs; + uint32_t value; +}; + +union TileXRCcuHccpJfsFlag { + struct { + uint32_t lockFree : 1; + uint32_t errorSuspend : 1; + uint32_t outorderComp : 1; + uint32_t orderType : 8; + uint32_t multiPath : 1; + uint32_t reserved : 20; + } bs; + uint32_t value; +}; + +union TileXRCcuHccpCstmJfsFlag { + struct { + uint32_t sqCstm : 1; + uint32_t dbCstm : 1; + uint32_t dbCtlCstm : 1; + uint32_t reserved : 29; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpJettyQueCfgEx { + uint32_t buffSize; + uint64_t buffVa; +}; + +struct TileXRCcuHccpQpCreateAttr { + void* scqHandle; + void* rcqHandle; + void* srqHandle; + uint32_t sqDepth; + uint32_t rqDepth; + int transportMode; + union { + struct { + uint32_t mode; + uint32_t udpSport; + uint8_t trafficClass; + uint8_t sl; + uint8_t timeout; + uint8_t rnrRetry; + uint8_t retryCnt; + } rdma; + struct { + int mode; + uint32_t jettyId; + TileXRCcuHccpJettyFlag flag; + TileXRCcuHccpJfsFlag jfsFlag; + void* tokenIdHandle; + uint32_t tokenValue; + uint8_t priority; + uint8_t rnrRetry; + uint8_t errTimeout; + union { + struct { + TileXRCcuHccpJettyQueCfgEx sq; + bool piType; + TileXRCcuHccpCstmJfsFlag cstmFlag; + uint32_t sqebbNum; + } extMode; + struct { + bool lockFlag; + uint32_t sqeBufIdx; + } taCacheMode; + }; + } ub; + }; + uint32_t resv[16]; +}; + +struct TileXRCcuHccpQpKey { + uint8_t value[TILEXR_CCU_HCCP_QP_KEY_BYTES]; + uint8_t size; +}; + +struct TileXRCcuHccpQpCreateInfo { + TileXRCcuHccpQpKey key; + union { + struct { + uint32_t qpn; + } rdma; + struct { + uint32_t uasid; + uint32_t id; + uint64_t sqBuffVa; + uint64_t wqebbSize; + uint64_t dbAddr; + uint32_t dbTokenId; + uint64_t ciAddr; + } ub; + }; + uint64_t va; + uint32_t resv[16]; +}; + +union TileXRCcuHccpImportJettyFlag { + struct { + uint32_t tokenPolicy : 3; + uint32_t orderType : 8; + uint32_t shareTp : 1; + uint32_t reserved : 20; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpJettyImportExpCfg { + uint64_t tpHandle; + uint64_t peerTpHandle; + uint64_t tag; + uint32_t txPsn; + uint32_t rxPsn; + uint32_t rsv[16]; +}; + +struct TileXRCcuHccpQpImportInfo { + struct { + TileXRCcuHccpQpKey key; + union { + struct { + int mode; + uint32_t tokenValue; + int policy; + int type; + TileXRCcuHccpImportJettyFlag flag; + TileXRCcuHccpJettyImportExpCfg expImportCfg; + uint32_t tpType; + } ub; + }; + uint32_t resv[7]; + } in; + struct { + union { + struct { + uint64_t tjettyHandle; + uint32_t tpn; + } ub; + }; + uint32_t resv[8]; + } out; +}; + +union TileXRCcuHccpGetTpCfgFlag { + struct { + uint32_t ctp : 1; + uint32_t rtp : 1; + uint32_t utp : 1; + uint32_t uboe : 1; + uint32_t preDefined : 1; + uint32_t dynamicDefined : 1; + uint32_t reserved : 26; + } bs; + uint32_t value; +}; + +struct TileXRCcuHccpGetTpCfg { + TileXRCcuHccpGetTpCfgFlag flag; + int transMode; + TileXRCcuHccpEid localEid; + TileXRCcuHccpEid peerEid; +}; + +struct TileXRCcuHccpTpInfo { + uint64_t tpHandle; + uint32_t resv; +}; + +struct TileXRCcuRaInitConfig { + uint32_t phyId; + uint32_t nicPosition; + int hdcType; + bool enableHdcAsync; +}; + +struct TileXRCcuRtProcExtParam { + const char* paramInfo; + uint64_t paramLen; +}; + +struct TileXRCcuRtNetServiceOpenArgs { + TileXRCcuRtProcExtParam* extParamList; + uint64_t extParamCnt; +}; + +struct TileXRCcuTlvInitInfo { + int version; + uint32_t phyId; + uint32_t nicPosition; + uint32_t reserved[16]; +}; + +struct TileXRCcuTlvMsg { + uint32_t type; + uint32_t length; + char* data; +}; + +struct TileXRCcuEndpointRouteProviderResourceWindow { + uint64_t addr = 0; + uint64_t bytes = 0; + uint32_t tokenId = 0; + uint32_t rawTokenId = 0; + uint32_t tokenValue = 0; +}; + +struct TileXRCcuEndpointRouteProviderRoute { + uint8_t remoteEid[TILEXR_CCU_EID_BYTES] = {}; + uint32_t tpn = 0; + uint64_t doorbellVa = 0; + uint32_t doorbellTokenId = 0; + uint32_t doorbellTokenValue = 0; + uint32_t sqDepth = 0; + bool endpointRouteVerified = false; +}; + +using TileXRCcuRaCustomChannelFunc = int (*)( + TileXRCcuRaInfo info, + TileXRCcuCustomChannelIn* in, + TileXRCcuCustomChannelOut* out); + +using TileXRCcuRtGetDevicePhyIdByIndexFunc = int (*)(uint32_t logicDevId, uint32_t* phyId); +using TileXRCcuRtOpenNetServiceFunc = int (*)(const TileXRCcuRtNetServiceOpenArgs* args); +using TileXRCcuRtCloseNetServiceFunc = int (*)(); + +using TileXRCcuRaInitFunc = int (*)(TileXRCcuRaInitConfig* config); +using TileXRCcuRaDeinitFunc = int (*)(TileXRCcuRaInitConfig* config); +using TileXRCcuRaTlvInitFunc = int (*)(TileXRCcuTlvInitInfo* initInfo, uint32_t* bufferSize, void** tlvHandle); +using TileXRCcuRaTlvRequestFunc = int (*)( + void* tlvHandle, + uint32_t moduleType, + TileXRCcuTlvMsg* sendMsg, + TileXRCcuTlvMsg* recvMsg); +using TileXRCcuRaTlvDeinitFunc = int (*)(void* tlvHandle); +using TileXRCcuRaGetDevEidInfoNumFunc = int (*)(TileXRCcuRaInfo info, uint32_t* num); +using TileXRCcuRaGetDevEidInfoListFunc = int (*)( + TileXRCcuRaInfo info, + TileXRCcuHccpDevEidInfo list[], + uint32_t* num); +using TileXRCcuRaCtxInitFunc = int (*)( + TileXRCcuHccpCtxInitCfg* cfg, + TileXRCcuHccpCtxInitAttr* attr, + void** ctx); +using TileXRCcuRaCtxDeinitFunc = int (*)(void* ctx); +using TileXRCcuRaCtxTokenIdAllocFunc = int (*)( + void* ctx, + TileXRCcuHccpTokenId* token, + void** tokenHandle); +using TileXRCcuRaCtxTokenIdFreeFunc = int (*)(void* ctx, void* tokenHandle); +using TileXRCcuRaCtxLmemRegisterFunc = int (*)( + void* ctx, + TileXRCcuHccpMrRegInfo* mr, + void** lmemHandle); +using TileXRCcuRaCtxLmemUnregisterFunc = int (*)(void* ctx, void* lmemHandle); +using TileXRCcuRaGetSecRandomFunc = int (*)(TileXRCcuRaInfo* info, uint32_t* value); +using TileXRCcuRaCtxChanCreateFunc = int (*)(void* ctx, TileXRCcuHccpChanInfo* info, void** chanHandle); +using TileXRCcuRaCtxChanDestroyFunc = int (*)(void* ctx, void* chanHandle); +using TileXRCcuRaCtxCqCreateFunc = int (*)(void* ctx, TileXRCcuHccpCqInfo* info, void** cqHandle); +using TileXRCcuRaCtxCqDestroyFunc = int (*)(void* ctx, void* cqHandle); +using TileXRCcuRaCtxQpCreateFunc = int (*)( + void* ctx, + TileXRCcuHccpQpCreateAttr* attr, + TileXRCcuHccpQpCreateInfo* info, + void** qpHandle); +using TileXRCcuRaCtxQpDestroyFunc = int (*)(void* qpHandle); +using TileXRCcuRaCtxQpImportFunc = int (*)( + void* ctx, + TileXRCcuHccpQpImportInfo* info, + void** remoteQpHandle); +using TileXRCcuRaCtxQpUnimportFunc = int (*)(void* ctx, void* remoteQpHandle); +using TileXRCcuRaCtxQpBindFunc = int (*)(void* qpHandle, void* remoteQpHandle); +using TileXRCcuRaCtxQpUnbindFunc = int (*)(void* qpHandle); +using TileXRCcuRaGetTpInfoListAsyncFunc = int (*)( + void* ctx, + TileXRCcuHccpGetTpCfg* cfg, + TileXRCcuHccpTpInfo infoList[], + uint32_t* num, + void** reqHandle); +using TileXRCcuRaGetAsyncReqResultFunc = int (*)(void* reqHandle, int* reqResult); + +using TileXRCcuEndpointRouteProviderFunc = int (*)( + uint32_t devicePhyId, + const TileXRCcuEndpointRouteProviderResourceWindow* localResourceWindow, + TileXRCcuEndpointRouteProviderRoute* route); + +static_assert(sizeof(TileXRCcuCustomChannelIn::data.raw) == TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE, + "CCU custom channel input must match HCCP custom_chan_info_in data size"); +static_assert(sizeof(TileXRCcuCustomChannelOut::data.raw) == TILEXR_CCU_CUSTOM_CHAN_DATA_MAX_SIZE, + "CCU custom channel output must match HCCP custom_chan_info_out data size"); + +} // namespace TileXR + +#endif // TILEXR_CCU_HCCP_TYPES_H diff --git a/src/comm/ccu/tilexr_ccu_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_ra_custom_channel_provider.cpp b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp new file mode 100644 index 00000000..f364290d --- /dev/null +++ b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026 TileXR Project + * Licensed under the Apache License, Version 2.0 + */ + +#include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + +namespace TileXR { +namespace { + +void ResetReport(TileXRCcuRaCustomChannelProviderReport* report) +{ + if (report != nullptr) { + *report = TileXRCcuRaCustomChannelProviderReport{}; + } +} + +int Fail(TileXRCcuRaCustomChannelProviderReport* report, const std::string& message) +{ + if (report != nullptr) { + report->message = message; + } + return TILEXR_ERROR_PARA_CHECK_FAIL; +} + +void FillReport( + uint32_t devicePhyId, + bool initialized, + const std::string& message, + TileXRCcuRaCustomChannelProviderReport* report) +{ + if (report == nullptr) { + return; + } + report->devicePhyId = devicePhyId; + report->initialized = initialized; + report->message = message; +} + +} // namespace + +int TileXRCcuRaCustomChannelProvider::Init( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelFunc raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report) +{ + TileXRCcuRaCustomChannelInvoker invoker; + if (raCustomChannel != nullptr) { + invoker = + [raCustomChannel]( + TileXRCcuRaInfo info, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out) -> int { + return raCustomChannel( + info, + const_cast(&in), + out); + }; + } + return InitCallable(devicePhyId, invoker, report); +} + +int TileXRCcuRaCustomChannelProvider::InitCallable( + uint32_t devicePhyId, + TileXRCcuRaCustomChannelInvoker raCustomChannel, + TileXRCcuRaCustomChannelProviderReport* report) +{ + ResetReport(report); + if (!raCustomChannel) { + initialized_ = false; + return Fail(report, "missing RA custom channel function"); + } + devicePhyId_ = devicePhyId; + raCustomChannel_ = raCustomChannel; + initialized_ = true; + FillReport(devicePhyId_, initialized_, "ok", report); + return TILEXR_SUCCESS; +} + +int TileXRCcuRaCustomChannelProvider::CreateAdapter( + TileXRCcuDriverAdapter* adapter, + TileXRCcuDriverAdapterReport* report) +{ + if (adapter == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + if (!initialized_ || !raCustomChannel_) { + return TILEXR_ERROR_NOT_INITIALIZED; + } + return adapter->Init(devicePhyId_, &TileXRCcuRaCustomChannelProvider::AdapterCallback, this, report); +} + +int TileXRCcuRaCustomChannelProvider::AdapterCallback( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) +{ + auto* provider = static_cast(userData); + if (provider == nullptr || !provider->raCustomChannel_ || out == nullptr) { + return TILEXR_ERROR_PARA_CHECK_FAIL; + } + TileXRCcuRaInfo info {}; + info.mode = TILEXR_CCU_NETWORK_OFFLINE; + info.phyId = devicePhyId; + return provider->raCustomChannel_( + info, + in, + out); +} + +} // namespace TileXR diff --git a/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h b/src/comm/ccu/tilexr_ccu_ra_custom_channel_provider.h new file mode 100644 index 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/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/test_tilexr_ccu_driver_adapter.py b/tests/ccu/test_tilexr_ccu_driver_adapter.py new file mode 100644 index 00000000..c2b6863d --- /dev/null +++ b/tests/ccu/test_tilexr_ccu_driver_adapter.py @@ -0,0 +1,995 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 TileXR Project +# + +import shutil +import subprocess +import tempfile +import textwrap +import unittest +import os +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +DRIVER_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.h" +DRIVER_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_driver_adapter.cpp" +SPECS_HEADER = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.h" +SPECS_SOURCE = REPO_ROOT / "src" / "comm" / "ccu" / "tilexr_ccu_specs.cpp" +COMM_CMAKE = REPO_ROOT / "src" / "comm" / "CMakeLists.txt" +INCLUDE_DIR = REPO_ROOT / "src" / "include" +COMM_DIR = REPO_ROOT / "src" / "comm" + + +class TileXRCcuDriverAdapterTest(unittest.TestCase): + def compile_and_run(self, code: str, extra_env=None): + compiler = shutil.which("g++") or shutil.which("clang++") or shutil.which("c++") + if compiler is None: + self.skipTest("no local C++ compiler found") + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + test_cpp = temp_path / "driver_adapter_test.cpp" + test_bin = temp_path / "driver_adapter_test" + test_cpp.write_text(code, encoding="utf-8") + subprocess.run( + [ + compiler, + "-std=c++14", + "-I", + str(INCLUDE_DIR), + "-I", + str(COMM_DIR), + str(test_cpp), + str(DRIVER_SOURCE), + str(SPECS_SOURCE), + "-o", + str(test_bin), + ], + cwd=REPO_ROOT, + check=True, + text=True, + capture_output=True, + ) + env = None + if extra_env: + env = {**os.environ, **extra_env} + return subprocess.run( + [str(test_bin)], + cwd=REPO_ROOT, + check=False, + text=True, + capture_output=True, + env=env) + + def test_adapter_wraps_get_basic_info_and_reuses_tilexr_specs_decode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + if (out == nullptr) { + return -1; + } + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x45; + out->data.dataInfo.dataArray[0].baseinfo.tokenId = 0x1234; + out->data.dataInfo.dataArray[0].baseinfo.tokenValue = 0; + out->data.dataInfo.dataArray[0].baseinfo.tokenValid = 1; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x059b0f03U; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x200000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (3U << 24) | (5U << 16) | 255U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (127U << 16) | 63U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (31U << 16) | 15U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (7U << 16) | 1U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap4 = 9U; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(4, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(1, &basic, &report) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 4 || + state.observedOp != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.observedDie != 1) { + std::cerr << "custom channel request mismatch\n"; + return 3; + } + if (basic.dieId != 1 || basic.msId != 0x45 || basic.missionKey != 0x059b0f03U || + basic.resourceAddr != 0x200000000ULL || basic.caps.cap0 == 0 || + basic.msidToken.tokenId != 0x1234 || basic.msidToken.tokenValue != 0 || + !basic.msidToken.valid) { + std::cerr << "basic info mismatch\n"; + return 4; + } + + TileXRCcuSpecInfo info; + TileXRCcuSpecsReport specsReport; + if (TileXRCcuDecodeBasicInfo(basic, &info, &specsReport) != TILEXR_SUCCESS) { + std::cerr << "decode failed: " << specsReport.message << "\n"; + return 5; + } + if (info.instructionNum != 256 || info.xnNum != 128 || info.channelNum != 2 || + info.missionNum != 6 || info.loopEngineNum != 4) { + std::cerr << "decoded info mismatch\n"; + return 6; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_GET_BASIC_INFO || + report.dieId != 1 || report.devicePhyId != 4) { + std::cerr << "report mismatch\n"; + return 7; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_wraps_die_enable_and_reports_driver_errors(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + bool fail = false; + uint32_t observedOp = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->observedOp = in.op; + if (state->fail) { + return -22; + } + out->opRet = 0; + out->data.dataInfo.dataArray[0].dieinfo.enableFlag = TILEXR_CCU_ENABLE_FLAG; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(7, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed\n"; + return 1; + } + + bool enabled = false; + if (adapter.GetDieEnabled(0, &enabled, &report) != TILEXR_SUCCESS || !enabled) { + std::cerr << "die enable query failed: " << report.message << "\n"; + return 2; + } + if (state.observedOp != TILEXR_CCU_U_OP_GET_DIE_WORKING) { + std::cerr << "die opcode mismatch\n"; + return 3; + } + + state.fail = true; + if (adapter.GetDieEnabled(0, &enabled, &report) != TILEXR_ERROR_MKIRT) { + std::cerr << "driver failure was accepted\n"; + return 4; + } + if (report.message.find("CCU custom channel call failed") == std::string::npos || + report.message.find("driverRet=-22") == std::string::npos || + report.message.find("opRet=0") == std::string::npos || + report.message.find("op=15") == std::string::npos || + report.driverRet != -22) { + std::cerr << "weak driver diagnostic: " << report.message << "\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_installs_instruction_repository_with_set_instruction_opcode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint32_t observedArraySize = 0; + uint64_t observedResourceAddr = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedArraySize = in.data.dataInfo.dataArraySize; + state->observedResourceAddr = in.data.dataInfo.dataArray[0].insinfo.resourceAddr; + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + if (adapter.InstallInstructions(1, 489, 13, 0x100051152e00ULL, 13 * 32, &report) != + TILEXR_SUCCESS) { + std::cerr << "install failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 5 || + state.observedOp != TILEXR_CCU_U_OP_SET_INSTRUCTION || + state.observedDie != 1 || state.observedOffset != 489 || + state.observedDataLen != 13 * 32 || state.observedArraySize != 1 || + state.observedResourceAddr != 0x100051152e00ULL) { + std::cerr << "SET_INSTRUCTION request mismatch\n"; + return 3; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_SET_INSTRUCTION || + report.dieId != 1 || report.devicePhyId != 5) { + std::cerr << "report mismatch\n"; + return 4; + } + + if (adapter.InstallInstructions(1, 489, 13, 0, 13 * 32, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "zero device instruction address accepted\n"; + return 5; + } + if (adapter.InstallInstructions(1, 489, 0, 0x100051152e00ULL, 0, &report) != + TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "empty instruction image accepted\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_rejects_instruction_byte_mismatch_before_custom_channel(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + struct FakeState { + int calls = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + const int ret = adapter.InstallInstructions( + 1, + 489, + 13, + 0x100051152e00ULL, + 12 * TILEXR_CCU_INSTRUCTION_BYTES, + &report); + if (ret != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "byte mismatch accepted ret=" << ret << "\n"; + return 2; + } + if (state.calls != 0 || + report.message.find("byte size mismatch") == std::string::npos) { + std::cerr << "byte mismatch should fail before custom channel: " + << report.message << " calls=" << state.calls << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_reads_each_instruction_from_its_own_data_array_slot(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct InstructionWords { + uint64_t words[4]; + }; + + struct FakeState { + int calls = 0; + uint32_t observedDevice = 0; + uint32_t observedOp = 0; + uint32_t observedDie = 0; + uint32_t observedOffset = 0; + uint32_t observedDataLen = 0; + uint32_t observedArraySize = 0; + }; + + int FakeCustomChannel( + uint32_t devicePhyId, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + state->calls++; + state->observedDevice = devicePhyId; + state->observedOp = in.op; + state->observedDie = in.data.dataInfo.udieIdx; + state->observedOffset = in.offsetStartIdx; + state->observedDataLen = in.data.dataInfo.dataLen; + state->observedArraySize = in.data.dataInfo.dataArraySize; + out->opRet = 0; + + InstructionWords first {{0x1014c00010802ULL, 0, 0, 0}}; + InstructionWords second {{0x10804ULL, 0x1014cULL, 0, 0}}; + std::memcpy(out->data.dataInfo.dataArray[0].byte32.raw, &first, sizeof(first)); + std::memcpy(out->data.dataInfo.dataArray[1].byte32.raw, &second, sizeof(second)); + out->data.dataInfo.dataArraySize = 2; + out->data.dataInfo.dataLen = 2 * TILEXR_CCU_INSTRUCTION_BYTES; + out->offsetNextIdx = 491; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + InstructionWords instructions[2] {}; + if (adapter.ReadInstructions( + 1, + 489, + instructions, + 2, + 2 * TILEXR_CCU_INSTRUCTION_BYTES, + &report) != TILEXR_SUCCESS) { + std::cerr << "read failed: " << report.message << "\n"; + return 2; + } + if (state.calls != 1 || state.observedDevice != 5 || + state.observedOp != TILEXR_CCU_U_OP_GET_INSTRUCTION || + state.observedDie != 1 || state.observedOffset != 489 || + state.observedDataLen != 2 * TILEXR_CCU_INSTRUCTION_BYTES || + state.observedArraySize != 2) { + std::cerr << "GET_INSTRUCTION request mismatch\n"; + return 3; + } + if (instructions[0].words[0] != 0x1014c00010802ULL || + instructions[0].words[1] != 0 || + instructions[1].words[0] != 0x10804ULL || + instructions[1].words[1] != 0x1014cULL) { + std::cerr << "readback slot copy mismatch first=0x" << std::hex + << instructions[0].words[0] << " second0=0x" + << instructions[1].words[0] << " second1=0x" + << instructions[1].words[1] << "\n"; + return 4; + } + if (report.message != "ok" || report.opcode != TILEXR_CCU_U_OP_GET_INSTRUCTION || + report.dieId != 1 || report.devicePhyId != 5) { + std::cerr << "report mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_set_instruction_trailer_wire_word_is_offset_then_opcode(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct FakeState { + uint64_t trailer = 0; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + std::memcpy(&state->trailer, &in.offsetStartIdx, sizeof(state->trailer)); + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + if (adapter.InstallInstructions( + 1, 489, 13, 0x100051152e00ULL, 13 * TILEXR_CCU_INSTRUCTION_BYTES, &report) != + TILEXR_SUCCESS) { + std::cerr << "install failed: " << report.message << "\n"; + return 2; + } + const uint64_t expected = + (static_cast(TILEXR_CCU_U_OP_SET_INSTRUCTION) << 32U) | 489ULL; + if (state.trailer != expected) { + std::cerr << "trailer mismatch observed=0x" << std::hex << state.trailer + << " expected=0x" << expected << "\n"; + return 3; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_adapter_wraps_lower_layer_set_payloads_without_hcomm_runtime(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + #include + + using namespace TileXR; + + struct ObservedCall { + uint32_t op = 0; + uint32_t die = 0; + uint32_t offset = 0; + uint32_t dataLen = 0; + uint32_t arraySize = 0; + uint8_t raw[256] = {0}; + uint32_t msId = 0; + uint32_t tokenId = 0; + uint32_t tokenValue = 0; + }; + + struct FakeState { + int calls = 0; + ObservedCall observed[8]; + }; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn& in, + TileXRCcuCustomChannelOut* out, + void* userData) + { + auto* state = static_cast(userData); + if (state->calls >= 8) { + return -1; + } + auto& observed = state->observed[state->calls++]; + observed.op = in.op; + observed.die = in.data.dataInfo.udieIdx; + observed.offset = in.offsetStartIdx; + observed.dataLen = in.data.dataInfo.dataLen; + observed.arraySize = in.data.dataInfo.dataArraySize; + observed.msId = in.data.dataInfo.dataArray[0].baseinfo.msId; + observed.tokenId = in.data.dataInfo.dataArray[0].baseinfo.tokenId; + observed.tokenValue = in.data.dataInfo.dataArray[0].baseinfo.tokenValue; + std::memcpy(observed.raw, in.data.dataInfo.dataArray, sizeof(observed.raw)); + out->opRet = 0; + return 0; + } + + int main() + { + FakeState state; + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, &state, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + if (adapter.InstallMsidToken(1, 0x45, 0x1234, 0x5678, &report) != TILEXR_SUCCESS) { + std::cerr << "msid install failed: " << report.message << "\n"; + return 2; + } + if (state.observed[0].op != TILEXR_CCU_U_OP_SET_MSID_TOKEN || + state.observed[0].die != 1 || state.observed[0].offset != 0 || + state.observed[0].dataLen != 0 || state.observed[0].arraySize != 0 || + state.observed[0].msId != 0x45 || state.observed[0].tokenId != 0x1234 || + state.observed[0].tokenValue != 0x5678) { + std::cerr << "SET_MSID_TOKEN request mismatch\n"; + return 3; + } + + TileXRCcuPfeCtx pfe{}; + for (uint32_t i = 0; i < sizeof(pfe.raw); ++i) { + pfe.raw[i] = static_cast(0xa0 + i); + } + if (adapter.InstallPfeCtx(1, 7, pfe, &report) != TILEXR_SUCCESS) { + std::cerr << "pfe install failed: " << report.message << "\n"; + return 4; + } + if (state.observed[1].op != TILEXR_CCU_U_OP_SET_PFE || + state.observed[1].die != 1 || state.observed[1].offset != 7 || + state.observed[1].dataLen != TILEXR_CCU_PFE_CTX_BYTES || + state.observed[1].arraySize != 1 || + std::memcmp(state.observed[1].raw, pfe.raw, TILEXR_CCU_PFE_CTX_BYTES) != 0) { + std::cerr << "SET_PFE request mismatch\n"; + return 5; + } + + TileXRCcuLocalJettyCtxData jettys[2]{}; + for (uint32_t i = 0; i < sizeof(jettys[0].raw); ++i) { + jettys[0].raw[i] = static_cast(0x10 + i); + jettys[1].raw[i] = static_cast(0x50 + i); + } + if (adapter.InstallJettyCtx(1, 9, jettys, 2, &report) != TILEXR_SUCCESS) { + std::cerr << "jetty install failed: " << report.message << "\n"; + return 6; + } + if (state.observed[2].op != TILEXR_CCU_U_OP_SET_JETTY_CTX || + state.observed[2].die != 1 || state.observed[2].offset != 9 || + state.observed[2].dataLen != 2 * TILEXR_CCU_LOCAL_JETTY_CTX_BYTES || + state.observed[2].arraySize != 2 || + std::memcmp(state.observed[2].raw, jettys[0].raw, TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0 || + std::memcmp( + state.observed[2].raw + TILEXR_CCU_DATA_ARRAY_SLOT_BYTES, + jettys[1].raw, + TILEXR_CCU_LOCAL_JETTY_CTX_BYTES) != 0) { + std::cerr << "SET_JETTY_CTX request mismatch\n"; + return 7; + } + + TileXRCcuChannelCtxDataV1 channel{}; + for (uint32_t i = 0; i < sizeof(channel.raw); ++i) { + channel.raw[i] = static_cast(0xc0 + i); + } + if (adapter.InstallChannelCtxV1(1, 11, channel, &report) != TILEXR_SUCCESS) { + std::cerr << "channel install failed: " << report.message << "\n"; + return 8; + } + if (state.observed[3].op != TILEXR_CCU_U_OP_SET_CHANNEL || + state.observed[3].die != 1 || state.observed[3].offset != 11 || + state.observed[3].dataLen != TILEXR_CCU_CHANNEL_CTX_V1_BYTES || + state.observed[3].arraySize != 1 || + std::memcmp(state.observed[3].raw, channel.raw, TILEXR_CCU_CHANNEL_CTX_V1_BYTES) != 0) { + std::cerr << "SET_CHANNEL request mismatch\n"; + return 9; + } + + if (adapter.ClearCkeRange(1, 16, 10, &report) != TILEXR_SUCCESS) { + std::cerr << "cke clear failed: " << report.message << "\n"; + return 10; + } + if (state.observed[4].op != TILEXR_CCU_U_OP_SET_CKE || + state.observed[4].offset != 16 || state.observed[4].arraySize != 8 || + state.observed[4].dataLen != 8 * TILEXR_CCU_CKE_SLOT_BYTES || + state.observed[5].op != TILEXR_CCU_U_OP_SET_CKE || + state.observed[5].offset != 24 || state.observed[5].arraySize != 2 || + state.observed[5].dataLen != 2 * TILEXR_CCU_CKE_SLOT_BYTES) { + std::cerr << "SET_CKE batching mismatch\n"; + return 11; + } + + if (adapter.InstallXnRange(1, 32, 10, &report) != TILEXR_SUCCESS) { + std::cerr << "xn install failed: " << report.message << "\n"; + return 12; + } + if (state.observed[6].op != TILEXR_CCU_U_OP_SET_XN || + state.observed[6].offset != 32 || state.observed[6].arraySize != 8 || + state.observed[6].dataLen != 8 * TILEXR_CCU_XN_SLOT_BYTES || + state.observed[7].op != TILEXR_CCU_U_OP_SET_XN || + state.observed[7].offset != 40 || state.observed[7].arraySize != 2 || + state.observed[7].dataLen != 2 * TILEXR_CCU_XN_SLOT_BYTES) { + std::cerr << "SET_XN batching mismatch\n"; + return 13; + } + + if (adapter.InstallJettyCtx(1, 9, jettys, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.InstallJettyCtx(1, 9, nullptr, 2, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.ClearCkeRange(1, 0, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL || + adapter.InstallXnRange(1, 0, 0, &report) != TILEXR_ERROR_PARA_CHECK_FAIL) { + std::cerr << "invalid lower-layer payload accepted\n"; + return 14; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_msid_token_envelope_intentionally_matches_hcomm_zero_length_reference(self): + hcomm_path = ( + REPO_ROOT + / "3rdparty" + / "hcomm" + / "src" + / "framework" + / "next" + / "comms" + / "ccu" + / "ccu_device" + / "ccu_comp" + / "ccu_comp.cc" + ) + if not hcomm_path.exists(): + self.skipTest("comparison-only hcomm checkout is unavailable") + hcomm_source = hcomm_path.read_text(encoding="utf-8") + hcomm_body = hcomm_source[ + hcomm_source.index("HcclResult CcuComponent::ConfigMsIdToken()") : + hcomm_source.index("HcclResult CcuComponent::GetCcuResourceSpaceBufInfo") + ] + source = DRIVER_SOURCE.read_text(encoding="utf-8") + tilexr_body = source[ + source.index("int TileXRCcuDriverAdapter::InstallMsidToken(") : + source.index("int TileXRCcuDriverAdapter::InstallPfeCtx(") + ] + + for needle in [ + "CCU_U_OP_SET_MSID_TOKEN", + "baseinfo.msId", + "baseinfo.tokenId", + "baseinfo.tokenValue", + ]: + with self.subTest(reference=needle): + self.assertIn(needle, hcomm_body) + self.assertIn(needle, tilexr_body) + + self.assertNotIn("dataArraySize", hcomm_body) + self.assertNotIn("dataLen", hcomm_body) + self.assertNotIn("dataArraySize", tilexr_body) + self.assertNotIn("dataLen", tilexr_body) + + def test_direct_trace_dumps_custom_channel_envelope_and_payload_words(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TILEXR_CCU_DIRECT_TRACE", + "TraceCustomChannelRequest", + "TileXRDirectCcuTrace customChannel", + "devicePhyId=", + "op=", + "dieId=", + "offset=", + "dataLen=", + "arraySize=", + "payloadWords=", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_direct_trace_dumps_custom_channel_return_and_trailer_fields(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + for needle in [ + "TraceCustomChannelReturn", + "TileXRDirectCcuTrace customChannel.return", + "driverRet=", + "opRet=", + "offsetNext=", + "customChannel.requestTrailer", + "customChannel.response", + ]: + with self.subTest(needle=needle): + self.assertIn(needle, source) + + def test_direct_trace_runtime_emits_custom_channel_request_payload(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->opRet = 0; + return 0; + } + + int main() + { + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, nullptr, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + TileXRCcuChannelCtxDataV1 channel{}; + for (uint32_t i = 0; i < TILEXR_CCU_CHANNEL_CTX_V1_BYTES; ++i) { + channel.raw[i] = static_cast(0x10 + i); + } + if (adapter.InstallChannelCtxV1(1, 11, channel, &report) != TILEXR_SUCCESS) { + std::cerr << "channel install failed: " << report.message << "\n"; + return 2; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code, {"TILEXR_CCU_DIRECT_TRACE": "1"}) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace customChannel", result.stderr) + self.assertIn("devicePhyId=5", result.stderr) + self.assertIn("op=256", result.stderr) + self.assertIn("dieId=1", result.stderr) + self.assertIn("offset=11", result.stderr) + self.assertIn("dataLen=64", result.stderr) + self.assertIn("arraySize=1", result.stderr) + self.assertIn("payloadWords=8", result.stderr) + self.assertIn("customChannel.payloadWords=8", result.stderr) + self.assertIn("w0=0x1716151413121110", result.stderr) + + def test_direct_trace_runtime_emits_custom_channel_return_and_trailer_fields(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_driver_adapter.h" + + #include + + using namespace TileXR; + + int FakeCustomChannel( + uint32_t, + const TileXRCcuCustomChannelIn&, + TileXRCcuCustomChannelOut* out, + void*) + { + out->offsetNextIdx = 99; + out->opRet = 7; + out->data.dataInfo.dataArray[0].dieinfo.enableFlag = 0; + return -22; + } + + int main() + { + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport report; + if (adapter.Init(5, FakeCustomChannel, nullptr, &report) != TILEXR_SUCCESS) { + std::cerr << "init failed: " << report.message << "\n"; + return 1; + } + + bool enabled = true; + const int ret = adapter.GetDieEnabled(1, &enabled, &report); + if (ret != TILEXR_ERROR_MKIRT) { + std::cerr << "driver failure was accepted\n"; + return 2; + } + if (report.driverRet != -22 || report.opRet != 7 || + report.opcode != TILEXR_CCU_U_OP_GET_DIE_WORKING) { + std::cerr << "report did not retain driver diagnostics\n"; + return 3; + } + if (report.message.find("driverRet=-22") == std::string::npos || + report.message.find("opRet=7") == std::string::npos || + report.message.find("op=15") == std::string::npos) { + std::cerr << "message did not retain driver diagnostics: " << report.message << "\n"; + return 4; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code, {"TILEXR_CCU_DIRECT_TRACE": "1"}) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertIn("TileXRDirectCcuTrace customChannel.return", result.stderr) + self.assertIn("driverRet=-22", result.stderr) + self.assertIn("opRet=7", result.stderr) + self.assertIn("offsetNext=99", result.stderr) + self.assertIn("op=15", result.stderr) + self.assertIn("customChannel.requestTrailerWords=1", result.stderr) + self.assertIn("customChannel.responseWords=", result.stderr) + + def test_driver_adapter_failure_message_includes_opcode_and_driver_status(self): + source = DRIVER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("CcuCustomChannelFailureMessage", source) + self.assertIn('"CCU custom channel call failed"', source) + self.assertIn('"CCU custom channel operation failed"', source) + self.assertIn('" op="', source) + self.assertIn('" driverRet="', source) + self.assertIn('" opRet="', source) + self.assertIn( + "CcuCustomChannelFailureMessage(\"CCU custom channel call failed\", opcode, driverRet, out->opRet)", + source, + ) + self.assertIn( + "CcuCustomChannelFailureMessage(\"CCU custom channel operation failed\", opcode, driverRet, out->opRet)", + source, + ) + + def test_driver_adapter_is_wired_and_does_not_reference_hcomm_runtime_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = DRIVER_HEADER.read_text(encoding="utf-8") + source = DRIVER_SOURCE.read_text(encoding="utf-8") + specs_header = SPECS_HEADER.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_driver_adapter.h", cmake) + self.assertIn("ccu/tilexr_ccu_driver_adapter.cpp", cmake) + self.assertIn("TileXRCcuDriverAdapter", header) + self.assertIn("TileXRCcuMsidTokenInfo", specs_header) + self.assertIn("TileXRCcuMsidTokenInfo msidToken", specs_header) + self.assertIn("basicInfo->msidToken.tokenId = raw.tokenId", source) + self.assertIn("basicInfo->msidToken.tokenValue = raw.tokenValue", source) + self.assertIn("basicInfo->msidToken.valid = raw.tokenValid != 0", source) + self.assertIn("TileXRCcuCustomChannelIn", header) + self.assertIn("TILEXR_CCU_U_OP_GET_BASIC_INFO", header) + self.assertIn("TILEXR_CCU_U_OP_GET_DIE_WORKING", header) + self.assertIn("TILEXR_CCU_U_OP_SET_MSID_TOKEN", header) + self.assertIn("TILEXR_CCU_U_OP_SET_INSTRUCTION", header) + self.assertIn("TILEXR_CCU_U_OP_SET_XN", header) + self.assertIn("TILEXR_CCU_U_OP_SET_CKE", header) + self.assertIn("TILEXR_CCU_U_OP_SET_PFE", header) + self.assertIn("TILEXR_CCU_U_OP_SET_CHANNEL", header) + self.assertIn("TILEXR_CCU_U_OP_SET_JETTY_CTX", header) + self.assertIn("TILEXR_CCU_XN_SLOT_BYTES", header) + self.assertIn("TileXRCcuPfeCtx", header) + self.assertIn("TileXRCcuLocalJettyCtxData", header) + self.assertIn("TileXRCcuChannelCtxDataV1", header) + self.assertIn("TileXRCcuCustomChannelFn", header) + self.assertIn("GetBasicInfo", header) + self.assertIn("GetDieEnabled", header) + self.assertIn("InstallInstructions", header) + self.assertIn("InstallMsidToken", header) + self.assertIn("InstallPfeCtx", header) + self.assertIn("InstallJettyCtx", header) + self.assertIn("InstallChannelCtxV1", header) + self.assertIn("ClearCkeRange", header) + self.assertIn("InstallXnRange", header) + + combined = header + "\n" + source + for needle in [ + "#include + #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 + + using namespace TileXR; + + void* g_tilexrCcuRaProviderTestState = nullptr; + + struct FakeRaState { + uint32_t phyId = 0; + uint32_t mode = 0; + uint32_t op = 0; + uint32_t die = 0; + }; + + int FakeRaCustomChannel( + TileXRCcuRaInfo info, + TileXRCcuCustomChannelIn* in, + TileXRCcuCustomChannelOut* out) + { + auto* state = static_cast(g_tilexrCcuRaProviderTestState); + state->phyId = info.phyId; + state->mode = info.mode; + state->op = in->op; + state->die = in->data.dataInfo.udieIdx; + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x66; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x0badcafeU; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x300000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (1U << 24) | (2U << 16) | 31U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (15U << 16) | 7U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (3U << 16) | 5U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (9U << 16) | 1U; + return 0; + } + + int main() + { + FakeRaState state; + g_tilexrCcuRaProviderTestState = &state; + + TileXRCcuRaCustomChannelProvider provider; + TileXRCcuRaCustomChannelProviderReport providerReport; + if (provider.Init(9, FakeRaCustomChannel, &providerReport) != TILEXR_SUCCESS) { + std::cerr << "provider init failed: " << providerReport.message << "\n"; + return 1; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + if (provider.CreateAdapter(&adapter, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "create adapter failed: " << adapterReport.message << "\n"; + return 2; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(1, &basic, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << adapterReport.message << "\n"; + return 3; + } + if (state.phyId != 9 || state.mode != TILEXR_CCU_NETWORK_OFFLINE || + state.op != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.die != 1) { + std::cerr << "RA call mismatch\n"; + return 4; + } + if (basic.missionKey != 0x0badcafeU || basic.resourceAddr != 0x300000000ULL || + basic.msId != 0x66) { + std::cerr << "basic info mismatch\n"; + return 5; + } + if (providerReport.message != "ok" || providerReport.devicePhyId != 9) { + std::cerr << "provider report mismatch\n"; + return 6; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_accepts_opaque_ra_custom_channel_c_abi_shape(self): + code = textwrap.dedent( + r''' + #include "ccu/tilexr_ccu_ra_custom_channel_provider.h" + + #include + + using namespace TileXR; + + void* g_tilexrCcuRaProviderTestState = nullptr; + + struct LegacyRaInfo { + int mode = 0; + uint32_t phyId = 0; + }; + + struct FakeRaState { + uint32_t phyId = 0; + uint32_t mode = 0; + uint32_t op = 0; + uint32_t die = 0; + }; + + int FakeOpaqueRaCustomChannel(LegacyRaInfo info, void* rawIn, void* rawOut) + { + auto* state = static_cast(g_tilexrCcuRaProviderTestState); + auto* in = static_cast(rawIn); + auto* out = static_cast(rawOut); + state->phyId = info.phyId; + state->mode = info.mode; + state->op = in->op; + state->die = in->data.dataInfo.udieIdx; + out->opRet = 0; + out->data.dataInfo.dataArray[0].baseinfo.msId = 0x77; + out->data.dataInfo.dataArray[0].baseinfo.missionKey = 0x12345678U; + out->data.dataInfo.dataArray[0].baseinfo.resourceAddr = 0x400000000ULL; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap0 = (1U << 24) | (2U << 16) | 31U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap1 = (15U << 16) | 7U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap2 = (3U << 16) | 5U; + out->data.dataInfo.dataArray[0].baseinfo.caps.cap3 = (9U << 16) | 1U; + return 0; + } + + int main() + { + FakeRaState state; + g_tilexrCcuRaProviderTestState = &state; + + TileXRCcuRaCustomChannelProvider provider; + TileXRCcuRaCustomChannelProviderReport providerReport; + if (provider.Init(13, FakeOpaqueRaCustomChannel, &providerReport) != TILEXR_SUCCESS) { + std::cerr << "provider init failed: " << providerReport.message << "\n"; + return 1; + } + + TileXRCcuDriverAdapter adapter; + TileXRCcuDriverAdapterReport adapterReport; + if (provider.CreateAdapter(&adapter, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "create adapter failed: " << adapterReport.message << "\n"; + return 2; + } + + TileXRCcuBasicInfo basic; + if (adapter.GetBasicInfo(2, &basic, &adapterReport) != TILEXR_SUCCESS) { + std::cerr << "get basic info failed: " << adapterReport.message << "\n"; + return 3; + } + if (state.phyId != 13 || state.mode != TILEXR_CCU_NETWORK_OFFLINE || + state.op != TILEXR_CCU_U_OP_GET_BASIC_INFO || state.die != 2) { + std::cerr << "RA call mismatch\n"; + return 4; + } + if (basic.missionKey != 0x12345678U || basic.resourceAddr != 0x400000000ULL || + basic.msId != 0x77) { + std::cerr << "basic info mismatch\n"; + return 5; + } + return 0; + } + ''' + ) + + result = self.compile_and_run(code) + + self.assertEqual("", result.stderr) + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + def test_provider_is_wired_and_keeps_hcomm_runtime_out_of_ccu_surface(self): + cmake = COMM_CMAKE.read_text(encoding="utf-8") + header = PROVIDER_HEADER.read_text(encoding="utf-8") + source = PROVIDER_SOURCE.read_text(encoding="utf-8") + + self.assertIn("ccu/tilexr_ccu_ra_custom_channel_provider.h", cmake) + self.assertIn("ccu/tilexr_ccu_ra_custom_channel_provider.cpp", cmake) + self.assertIn("TileXRCcuRaCustomChannelProvider", header) + self.assertIn("CreateAdapter", header) + self.assertIn("TileXRCcuRaCustomChannelFunc", header) + self.assertIn("std::function", header) + self.assertIn("TILEXR_CCU_NETWORK_OFFLINE", source) + self.assertIn("TileXRCcuDriverAdapter", header) + self.assertNotIn("udma/", header) + + combined = header + "\n" + source + for needle in [ + "#include