已合并
修复qbmmV4perblock kernal流水同步错误和tilingkey拷贝错误 #3430
HKFLYE创建于 4月1日
修复qbmmV4perblock kernal流水同步错误和tilingkey拷贝错误 #3430
已合并
共 4 个文件变更+270-6
| @@ -0,0 +1,264 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | +* \file test_aclnn_quant_matmul_v5_perblock.cpp | ||
| 13 | +* \brief Test case for perblock quantization matmul | ||
| 14 | +*/ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + do { \ | ||
| 25 | + if (!(cond)) { \ | ||
| 26 | + return_expr; \ | ||
| 27 | + } \ | ||
| 28 | + } while (0) | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + do { \ | ||
| 32 | + if (!(cond)) { \ | ||
| 33 | + Finalize(deviceId, stream); \ | ||
| 34 | + return_expr; \ | ||
| 35 | + } \ | ||
| 36 | + } while (0) | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + do { \ | ||
| 40 | + printf(message, ##__VA_ARGS__); \ | ||
| 41 | + } while (0) | ||
| 42 | + | ||
| 43 | +int64_t GetShapeSize(const std::vector<int64_t>& shape) | ||
| 44 | +{ | ||
| 45 | + int64_t shapeSize = 1; | ||
| 46 | + for (auto i : shape) { | ||
| 47 | + shapeSize *= i; | ||
| 48 | + } | ||
| 49 | + return shapeSize; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +int Init(int32_t deviceId, aclrtStream* stream) | ||
| 53 | +{ | ||
| 54 | + // 固定写法,资源初始化 | ||
| 55 | + auto ret = aclInit(nullptr); | ||
| 56 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); | ||
| 57 | + ret = aclrtSetDevice(deviceId); | ||
| 58 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); | ||
| 59 | + ret = aclrtCreateStream(stream); | ||
| 60 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); | ||
| 61 | + return 0; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +template <typename T> | ||
| 65 | +int CreateAclTensor( | ||
| 66 | + const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType, | ||
| 67 | + aclTensor** tensor) | ||
| 68 | +{ | ||
| 69 | + auto size = GetShapeSize(shape) * sizeof(T); | ||
| 70 | + // 调用aclrtMalloc申请device侧内存 | ||
| 71 | + auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 72 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); | ||
| 73 | + // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 | ||
| 74 | + ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 75 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); | ||
| 76 | + | ||
| 77 | + // 计算连续tensor的strides | ||
| 78 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 79 | + for (int64_t i = shape.size() - 2; i >= 0; i--) { | ||
| 80 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + // 调用aclCreateTensor接口创建aclTensor | ||
| 84 | + *tensor = aclCreateTensor( | ||
| 85 | + shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), | ||
| 86 | + *deviceAddr); | ||
| 87 | + return 0; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +void Finalize(int32_t deviceId, aclrtStream stream) | ||
| 91 | +{ | ||
| 92 | + aclrtDestroyStream(stream); | ||
| 93 | + aclrtResetDevice(deviceId); | ||
| 94 | + aclFinalize(); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +/** | ||
| 98 | + * Test case for perblock quantization matmul | ||
| 99 | + * | ||
| 100 | + * Test configuration (satisfying all perblock tiling constraints): | ||
| 101 | + * - M = 128, K = 4096, N = 512 | ||
| 102 | + * - groupSizeK = 128, groupSizeN = 128 | ||
| 103 | + * - transposeX1 = false, transposeX2 = true | ||
| 104 | + * - x1: [M, K] = [128, 4096], dtype = INT8 | ||
| 105 | + * - x2: [N, K] = [512, 4096] (transposed), dtype = INT8 | ||
| 106 | + * - x1Scale: [M, CeilDiv(K, 128)] = [128, 32], dtype = FLOAT | ||
| 107 | + * - x2Scale: [CeilDiv(N, 128), CeilDiv(K, 128)] = [4, 32], dtype = FLOAT | ||
| 108 | + * - bias: [N] = [512], dtype = FLOAT | ||
| 109 | + * - output: [M, N] = [128, 512], dtype = BF16 | ||
| 110 | + * - N % 256 == 0 (satisfied: 512 % 256 == 0) | ||
| 111 | + */ | ||
| 112 | +int AclnnQuantMatmulV5PerblockTest(int32_t deviceId, aclrtStream& stream) | ||
| 113 | +{ | ||
| 114 | + auto ret = Init(deviceId, &stream); | ||
| 115 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); | ||
| 116 | + | ||
| 117 | + // Perblock tiling constraints: | ||
| 118 | + // - groupSizeK = 128, groupSizeN = 128 | ||
| 119 | + // - transposeX1 = false, transposeX2 = true | ||
| 120 | + // - N % 256 == 0 | ||
| 121 | + constexpr int64_t M = 128; | ||
| 122 | + constexpr int64_t K = 4096; | ||
| 123 | + constexpr int64_t N = 512; // N must be multiple of 256 (baseN) | ||
| 124 | + constexpr int64_t groupSizeM = 1; | ||
| 125 | + constexpr int64_t groupSizeK = 128; | ||
| 126 | + constexpr int64_t groupSizeN = 128; | ||
| 127 | + | ||
| 128 | + // x1: [M, K], dtype = INT8 | ||
| 129 | + std::vector<int64_t> x1Shape = {M, K}; | ||
| 130 | + // x2: [N, K] (transposed, so shape is [N, K]), dtype = INT8 | ||
| 131 | + std::vector<int64_t> x2Shape = {N, K}; | ||
| 132 | + // x1Scale: [M, CeilDiv(K, groupSizeK)], dtype = FLOAT | ||
| 133 | + std::vector<int64_t> x1ScaleShape = {M, (K + groupSizeK - 1) / groupSizeK}; // [128, 32] | ||
| 134 | + // x2Scale: [CeilDiv(N, groupSizeN), CeilDiv(K, groupSizeK)] (transposed), dtype = FLOAT | ||
| 135 | + std::vector<int64_t> x2ScaleShape = {(N + groupSizeN - 1) / groupSizeN, (K + groupSizeK - 1) / groupSizeK}; // [4, 32] | ||
| 136 | + // bias: [N], dtype = FLOAT | ||
| 137 | + std::vector<int64_t> biasShape = {N}; | ||
| 138 | + // output: [M, N], dtype = BF16 | ||
| 139 | + std::vector<int64_t> outShape = {M, N}; | ||
| 140 | + | ||
| 141 | + void* x1DeviceAddr = nullptr; | ||
| 142 | + void* x2DeviceAddr = nullptr; | ||
| 143 | + void* x2ScaleDeviceAddr = nullptr; | ||
| 144 | + void* x1ScaleDeviceAddr = nullptr; | ||
| 145 | + void* biasDeviceAddr = nullptr; | ||
| 146 | + void* outDeviceAddr = nullptr; | ||
| 147 | + aclTensor* x1 = nullptr; | ||
| 148 | + aclTensor* x2 = nullptr; | ||
| 149 | + aclTensor* bias = nullptr; | ||
| 150 | + aclTensor* x2Scale = nullptr; | ||
| 151 | + aclTensor* x1Scale = nullptr; | ||
| 152 | + aclTensor* out = nullptr; | ||
| 153 | + | ||
| 154 | + // Initialize input data | ||
| 155 | + std::vector<int8_t> x1HostData(GetShapeSize(x1Shape), 1); | ||
| 156 | + std::vector<int8_t> x2HostData(GetShapeSize(x2Shape), 1); | ||
| 157 | + std::vector<float> x1ScaleHostData(GetShapeSize(x1ScaleShape), 1.0f); | ||
| 158 | + std::vector<float> x2ScaleHostData(GetShapeSize(x2ScaleShape), 1.0f); | ||
| 159 | + std::vector<float> biasHostData(GetShapeSize(biasShape), 0.0f); | ||
| 160 | + std::vector<uint16_t> outHostData(GetShapeSize(outShape), 0); | ||
| 161 | + | ||
| 162 | + // 创建x1 aclTensor (INT8) | ||
| 163 | + ret = CreateAclTensor(x1HostData, x1Shape, &x1DeviceAddr, aclDataType::ACL_INT8, &x1); | ||
| 164 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x1TensorPtr(x1, aclDestroyTensor); | ||
| 165 | + std::unique_ptr<void, aclError (*)(void*)> x1DeviceAddrPtr(x1DeviceAddr, aclrtFree); | ||
| 166 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 167 | + | ||
| 168 | + // 创建x2 aclTensor (INT8) | ||
| 169 | + ret = CreateAclTensor(x2HostData, x2Shape, &x2DeviceAddr, aclDataType::ACL_INT8, &x2); | ||
| 170 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x2TensorPtr(x2, aclDestroyTensor); | ||
| 171 | + std::unique_ptr<void, aclError (*)(void*)> x2DeviceAddrPtr(x2DeviceAddr, aclrtFree); | ||
| 172 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 173 | + | ||
| 174 | + // 创建x1Scale aclTensor (FLOAT) | ||
| 175 | + ret = CreateAclTensor(x1ScaleHostData, x1ScaleShape, &x1ScaleDeviceAddr, aclDataType::ACL_FLOAT, &x1Scale); | ||
| 176 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x1ScaleTensorPtr(x1Scale, aclDestroyTensor); | ||
| 177 | + std::unique_ptr<void, aclError (*)(void*)> x1ScaleDeviceAddrPtr(x1ScaleDeviceAddr, aclrtFree); | ||
| 178 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 179 | + | ||
| 180 | + // 创建x2Scale aclTensor (FLOAT) | ||
| 181 | + ret = CreateAclTensor(x2ScaleHostData, x2ScaleShape, &x2ScaleDeviceAddr, aclDataType::ACL_FLOAT, &x2Scale); | ||
| 182 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x2ScaleTensorPtr(x2Scale, aclDestroyTensor); | ||
| 183 | + std::unique_ptr<void, aclError (*)(void*)> x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree); | ||
| 184 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 185 | + | ||
| 186 | + // 创建bias aclTensor (FLOAT) | ||
| 187 | + ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_FLOAT, &bias); | ||
| 188 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> biasTensorPtr(bias, aclDestroyTensor); | ||
| 189 | + std::unique_ptr<void, aclError (*)(void*)> biasDeviceAddrPtr(biasDeviceAddr, aclrtFree); | ||
| 190 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 191 | + | ||
| 192 | + // 创建out aclTensor (BF16) | ||
| 193 | + ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_BF16, &out); | ||
| 194 | + std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> outTensorPtr(out, aclDestroyTensor); | ||
| 195 | + std::unique_ptr<void, aclError (*)(void*)> outDeviceAddrPtr(outDeviceAddr, aclrtFree); | ||
| 196 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 197 | + | ||
| 198 | + // Perblock requires: transposeX1 = false, transposeX2 = true | ||
| 199 | + bool transposeX1 = false; | ||
| 200 | + bool transposeX2 = true; | ||
| 201 | + // groupSize encoding: groupSizeM in high bits, groupSizeN in middle, groupSizeK in low | ||
| 202 | + int64_t groupSize = (groupSizeM << 32) | (groupSizeN << 16) | groupSizeK; | ||
| 203 | + | ||
| 204 | + // 3. 调用CANN算子库API | ||
| 205 | + uint64_t workspaceSize = 0; | ||
| 206 | + aclOpExecutor* executor = nullptr; | ||
| 207 | + | ||
| 208 | + LOG_PRINT("Testing perblock quantization matmul:\n"); | ||
| 209 | + LOG_PRINT(" M=%ld, K=%ld, N=%ld\n", M, K, N); | ||
| 210 | + LOG_PRINT(" x1Shape=[%ld, %ld], x2Shape=[%ld, %ld]\n", x1Shape[0], x1Shape[1], x2Shape[0], x2Shape[1]); | ||
| 211 | + LOG_PRINT(" x1ScaleShape=[%ld, %ld], x2ScaleShape=[%ld, %ld]\n", x1ScaleShape[0], x1ScaleShape[1], x2ScaleShape[0], x2ScaleShape[1]); | ||
| 212 | + LOG_PRINT(" biasShape=[%ld], outShape=[%ld, %ld]\n", biasShape[0], outShape[0], outShape[1]); | ||
| 213 | + LOG_PRINT(" transposeX1=%d, transposeX2=%d, groupSize=0x%lx\n", transposeX1, transposeX2, groupSize); | ||
| 214 | + | ||
| 215 | + ret = aclnnQuantMatmulV5GetWorkspaceSize( | ||
| 216 | + x1, x2, x1Scale, x2Scale, nullptr, nullptr, nullptr, nullptr, bias, transposeX1, transposeX2, groupSize, out, | ||
| 217 | + &workspaceSize, &executor); | ||
| 218 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); | ||
| 219 | + | ||
| 220 | + // 根据第一段接口计算出的workspaceSize申请device内存 | ||
| 221 | + void* workspaceAddr = nullptr; | ||
| 222 | + std::unique_ptr<void, aclError (*)(void*)> workspaceAddrPtr(nullptr, aclrtFree); | ||
| 223 | + if (workspaceSize > 0) { | ||
| 224 | + ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 225 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); | ||
| 226 | + workspaceAddrPtr.reset(workspaceAddr); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + // 调用aclnnQuantMatmulV5第二段接口 | ||
| 230 | + ret = aclnnQuantMatmulV5(workspaceAddr, workspaceSize, executor, stream); | ||
| 231 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5 failed. ERROR: %d\n", ret); return ret); | ||
| 232 | + | ||
| 233 | + // 4. 同步等待任务执行结束 | ||
| 234 | + ret = aclrtSynchronizeStream(stream); | ||
| 235 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); | ||
| 236 | + | ||
| 237 | + LOG_PRINT("Perblock test completed successfully! The sync event ID fix is working.\n"); | ||
| 238 | + | ||
| 239 | + // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧 | ||
| 240 | + auto size = GetShapeSize(outShape); | ||
| 241 | + std::vector<uint16_t> resultData(size, 0); | ||
| 242 | + ret = aclrtMemcpy( | ||
| 243 | + resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), | ||
| 244 | + ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 245 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); | ||
| 246 | + | ||
| 247 | + // Print first few results for verification | ||
| 248 | + int64_t printCount = (size < 10) ? size : 10; | ||
| 249 | + for (int64_t i = 0; i < printCount; i++) { | ||
| 250 | + LOG_PRINT("result[%ld] is: %u\n", i, resultData[i]); | ||
| 251 | + } | ||
| 252 | + return ACL_SUCCESS; | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +int main() | ||
| 256 | +{ | ||
| 257 | + // 1. device/stream初始化 | ||
| 258 | + int32_t deviceId = 0; | ||
| 259 | + aclrtStream stream; | ||
| 260 | + auto ret = AclnnQuantMatmulV5PerblockTest(deviceId, stream); | ||
| 261 | + CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("AclnnQuantMatmulV5PerblockTest failed. ERROR: %d\n", ret); return ret); | ||
| 262 | + Finalize(deviceId, stream); | ||
| 263 | + return 0; | ||
| 264 | +} | ||
| @@ -407,7 +407,7 @@ ge::graphStatus QuantBatchMatmulV4PerblockTiling::DoLibApiTiling() | |||
| 407 | size_t tilingDataSize = sizeof(QuantBatchMatmulV4PerblockTilingData); | 407 | size_t tilingDataSize = sizeof(QuantBatchMatmulV4PerblockTilingData); |
| 408 | context_->SetBlockDim(compileInfo_.aicNum); | 408 | context_->SetBlockDim(compileInfo_.aicNum); |
| 409 | context_->SetScheduleMode(1); // 独占全核,设置以后会让所有核空闲以后才启动,有多核同步指令需要做此设置避免影响整网其他算子 | 409 | context_->SetScheduleMode(1); // 独占全核,设置以后会让所有核空闲以后才启动,有多核同步指令需要做此设置避免影响整网其他算子 |
| 410 | - errno_t ret = memcpy_s(context_->GetRawTilingData()->GetData(), context_->GetRawTilingData()->GetCapacity(), reinterpret_cast<void *>(&tilingData_), tilingDataSize); | 410 | + errno_t ret = memcpy_s(context_->GetRawTilingData()->GetData(), context_->GetRawTilingData()->GetCapacity(), reinterpret_cast<void *>(tilingData_), tilingDataSize); |
| 411 | if (ret != EOK){ | 411 | if (ret != EOK){ |
| 412 | OP_LOGE(context_->GetNodeName(), "memcpy_s failed, ret=%d", ret); | 412 | OP_LOGE(context_->GetNodeName(), "memcpy_s failed, ret=%d", ret); |
| 413 | return ge::GRAPH_FAILED; | 413 | return ge::GRAPH_FAILED; |
| @@ -88,10 +88,10 @@ protected: | |||
| 88 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eAL1Pong12_); | 88 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eAL1Pong12_); |
| 89 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Ping12_); | 89 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Ping12_); |
| 90 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Pong12_); | 90 | GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Pong12_); |
| 91 | - GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eAL1Ping21_); | 91 | + GetTPipePtr()->ReleaseEventID<HardEvent::MTE2_MTE1>(eAL1Ping21_); |
| 92 | - GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eAL1Pong21_); | 92 | + GetTPipePtr()->ReleaseEventID<HardEvent::MTE2_MTE1>(eAL1Pong21_); |
| 93 | - GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Ping21_); | 93 | + GetTPipePtr()->ReleaseEventID<HardEvent::MTE2_MTE1>(eBL1Ping21_); |
| 94 | - GetTPipePtr()->ReleaseEventID<HardEvent::MTE1_MTE2>(eBL1Ping21_); | 94 | + GetTPipePtr()->ReleaseEventID<HardEvent::MTE2_MTE1>(eBL1Pong21_); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | __aicore__ inline void initEventID() | 97 | __aicore__ inline void initEventID() |
| @@ -278,7 +278,7 @@ private: | |||
| 278 | aL1PingFlag = !aL1PingFlag; | 278 | aL1PingFlag = !aL1PingFlag; |
| 279 | } | 279 | } |
| 280 | if ((kidx + 1) % stepkb_ == 0) { | 280 | if ((kidx + 1) % stepkb_ == 0) { |
| 281 | - SetFlag<HardEvent::MTE1_MTE2>(bL1PingFlag ? eBL1Ping21_ : eBL1Pong21_); | 281 | + SetFlag<HardEvent::MTE1_MTE2>(bL1PingFlag ? eBL1Ping12_ : eBL1Pong12_); |
| 282 | bL1PingFlag = !bL1PingFlag; | 282 | bL1PingFlag = !bL1PingFlag; |
| 283 | } | 283 | } |
| 284 | CrossCoreWaitFlag(wsPingFlag ? V2C_PING_FLAG : V2C_PONG_FLAG); | 284 | CrossCoreWaitFlag(wsPingFlag ? V2C_PING_FLAG : V2C_PONG_FLAG); |