/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

#include "kernel_operator.h"
#include "../../../../include/common/common.h"
#include "../../../../include/common/common_func.h"
#include "../../../../include/common/simd.h"
#include "../../../../include/common/iterator.h"
#include "../../../../include/common/mma.h"
#include "../../../../include/common/utils.h"

template <bool is_sync, bool trans_b, bool trans_c>
__aicore__ __inline__ __attribute__((overloadable, always_inline)) void fft_matmul_split_n_kernel(
    bool is_complex_split_out_zN, __gm__ float *__restrict__ gm_a, __gm__ float *__restrict__ gm_b,
    __gm__ float *__restrict__ gm_c, __gm__ float *__restrict__ workspace, __gm__ float *__restrict__ gm_auxil,
    __gm__ int32_t *gm_radix_list, int64_t batch_size, int64_t M, int64_t N, int64_t K, int64_t lda, int64_t ldb,
    int64_t ldc, int32_t tile_M0, int32_t tile_N0, int32_t tile_K0, int32_t step_index, int32_t step_len,
    int64_t per_N0 = 0, bool seq357 = false)
{
    // 除0整改
    if (tile_M0 == 0) {
        tile_M0 = 1;
    }
    if (tile_N0 == 0) {
        tile_N0 = 1;
    }
    if (tile_K0 == 0) {
        tile_K0 = 1;
    }
    // 当radix=3取消掉倒数第三次迭代vec的padding
    bool is_vec_padding = !((N * K / 2) % 3 == 0 && step_len >= 4);

    SET_FLAG(FIX, M, EVENT_ID0);
    SET_FLAG(FIX, M, EVENT_ID1);
    SET_FLAG(FIX, M, EVENT_ID2);
    SET_FLAG(FIX, M, EVENT_ID3);

    SET_FLAG(MTE1, MTE2, EVENT_ID2);
    SET_FLAG(MTE1, MTE2, EVENT_ID3);

    SET_FLAG(M, MTE1, EVENT_ID0);
    SET_FLAG(M, MTE1, EVENT_ID1);
    SET_FLAG(M, MTE1, EVENT_ID2);
    SET_FLAG(M, MTE1, EVENT_ID3);
    int64_t M_round = (M + 16 - 1) / 16 * 16;
    int64_t N_round = (N + 16 - 1) / 16 * 16;
    int64_t K_round = (K + 16 - 1) / 16 * 16;

    AscendC::GlobalTensor<float> gm_a_tensor;
    AscendC::GlobalTensor<float> gm_b_tensor;
    AscendC::GlobalTensor<float> gm_c_tensor;
    AscendC::GlobalTensor<float> workspace_tensor;
    AsdopsBuffer<ArchType::ASCEND_V220> buf;

    gm_a_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_a));
    gm_b_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_b));
    gm_c_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_c));
    workspace_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(workspace));

    AscendC::LocalTensor<float> l1_base_a = buf.GetBuffer<BufferType::ASCEND_CB, float>(0);
    AscendC::LocalTensor<float> l1_base_b= buf.GetBuffer<BufferType::ASCEND_CB, float>(256 * 1024);

    AscendC::LocalTensor<float> l0a_base = buf.GetBuffer<BufferType::ASCEND_L0A, float>(0);
    AscendC::LocalTensor<float> l0b_base = buf.GetBuffer<BufferType::ASCEND_L0B, float>(0);
    AscendC::LocalTensor<float> l0c_base = buf.GetBuffer<BufferType::ASCEND_L0C, float>(0);

    int32_t max_L1_B_buffer_num = L1_PINGPONG_BUFFER_LEN / 2;

    auto l1_buf_a = l1_base_a;
    auto l0a_buf = l0a_base;
    auto l0b_buf = l0b_base;

    bool ping_flag = 0;
    bool mte1_mad_ping_flag = 0;
    bool l0c_ping_flag = 0;

    int32_t batch_len = L0AB_PINGPONG_BUFFER_LEN * 2 / (tile_N0 * tile_K0);
    batch_len += (batch_len == 0);
    int32_t batch_remain = batch_size % batch_len;

    int64_t m_loop = (M + tile_M0 - 1) / tile_M0;
    if (m_loop == 0) {
        m_loop = 1;
    }
    int64_t n_loop = (N + tile_N0 - 1) / tile_N0;
    int64_t k_loop = (K + tile_K0 - 1) / tile_K0;
    int64_t batch_loop = (batch_size + batch_len - 1) / batch_len;
    if (batch_loop == 0) {
        batch_loop = 1;
    }
    int64_t loop = batch_loop * m_loop * n_loop;

    int64_t m_actual = M;
    int64_t m_round = (m_actual + 15) / 16 * 16;
    int64_t k_actual = K;
    int64_t k_round = (k_actual + 15) / 16 * 16;

    int32_t m0_round = m_round;
    int32_t m0_actual = m_actual;
    int32_t k0_round = k_round;
    int32_t k0_actual = k_actual;

    // 每次l0a中放的分块最大为128 * 64
    int32_t n_part_loop = 1;
    int32_t n_part_len = L0AB_PINGPONG_BUFFER_LEN / tile_K0 / 16 * 16;
    if (n_part_len > tile_N0 / 2 && n_part_len < tile_N0)
        n_part_len = MIN(n_part_len, ROUND(tile_N0 / 2, 16));
    n_part_len += (n_part_len == 0);
    n_part_len = MIN(n_part_len, N_round);

    int32_t batch_part_len = L0AB_PINGPONG_BUFFER_LEN / (tile_K0 * n_part_len);
    batch_part_len += (batch_part_len == 0);

    int32_t per_N1 = -1;
    if (step_index > 0 && step_len > 2 && gm_radix_list != nullptr) {
        per_N1 = gm_radix_list[step_index - 1];
    }

    // M * K < 256KB
    // *** load matrix A to L1
    load_matrix_zN(l1_buf_a, gm_a_tensor, tile_M0, tile_K0, m_actual, k_round, lda);

    SET_FLAG(MTE2, MTE1, EVENT_ID0);
    WAIT_FLAG(MTE2, MTE1, EVENT_ID0);

    if (trans_c) {
        auto l1_src_a = l1_buf_a;
        for (int32_t i = 0; i < k0_round / C0_SIZE; i++) {
            AscendC::LoadData(
                l0b_buf[i * m0_round * C0_SIZE],
                l1_src_a[i * C0_SIZE * tile_M0],
                AscendC::LoadData2dParams(
                    0,                   // baseIdx
                    m0_round / BLOCK_SIZE,  // repeat
                    0,                    // srcStride
                    0,                   // sid
                    1,                   // dstGap
                    false,               // transpose
                    inc                  // addr_cal_mode_t
                )
            );
        }
    } else {
        auto l1_src_a = l1_buf_a;
        for (int32_t i = 0; i < m0_round / BLOCK_SIZE; i++) {
            AscendC::LoadData(
                l0a_buf[i * k0_round * BLOCK_SIZE],
                l1_src_a[i * CUBE_MATRIX_SIZE],
                AscendC::LoadData2dParams(
                    0,                   // baseIdx
                    k0_round / C0_SIZE,  // repeat
                    tile_M0 / BLOCK_SIZE, // srcStride
                    0,                   // sid
                    0,                   // dstGap
                    false,               // transpose
                    inc                  // addr_cal_mode_t
                )
            );
        }
    }

    SET_FLAG(MTE1, M, EVENT_ID0);
    WAIT_FLAG(MTE1, M, EVENT_ID0);

    int64_t group_num = AscendC::GetBlockNum();
    if (group_num == 0) {
        group_num = 1;
    }
    int64_t group_id = AscendC::GetBlockIdx();

    int64_t loop_per_group;
    int64_t loop_per_group_remain;
    int64_t loop_per_group_actual;
    if (step_len > 3 && step_index == step_len - 3) {
        loop_per_group = loop / group_num;
        loop_per_group_remain = loop % group_num;
        loop_per_group_actual = loop_per_group;
        if (group_id < loop_per_group_remain) {
            loop_per_group_actual++;
        }
    } else {
        loop_per_group = (loop / m_loop) / group_num;
        loop_per_group_remain = (loop / m_loop) % group_num;
        loop_per_group_actual = loop_per_group;
        if (group_id < loop_per_group_remain) {
            loop_per_group_actual++;
        }
        loop_per_group_actual *= m_loop;
    }

    for (int64_t i = 0; i < loop_per_group_actual; i++) {
        int64_t loop_idx;
        int64_t batch_idx;
        int64_t batch_n_idx;
        int64_t m_n_idx;
        int64_t m_idx;
        int64_t n_idx;

        if (step_len > 3 && step_index == step_len - 3) {
            loop_idx =
                group_id * loop_per_group +
                (loop_per_group_remain > 0) * (group_id > loop_per_group_remain ? loop_per_group_remain : group_id) + i;
            batch_idx = loop_idx % batch_loop;
            m_n_idx = loop_idx / batch_loop;
            m_idx = m_n_idx / n_loop;
            n_idx = m_n_idx % n_loop;
        } else {
            loop_idx = group_id * loop_per_group * m_loop +
                       (loop_per_group_remain > 0) *
                           (group_id > loop_per_group_remain ? loop_per_group_remain : group_id) * m_loop +
                       i;
            m_idx = loop_idx % m_loop;
            batch_n_idx = loop_idx / m_loop;
            batch_idx = batch_n_idx % batch_loop;
            n_idx = batch_n_idx / batch_loop;
        }

        int64_t offset_b;
        int32_t n_actual = (n_idx == (n_loop - 1)) ? (N - n_idx * tile_N0) : tile_N0;
        int32_t n_round = (n_actual + 15) / 16 * 16;

        n_part_loop = (n_actual + n_part_len - 1) / n_part_len;

        int32_t batch_actual = batch_len;
        if (batch_idx == batch_loop - 1 && batch_remain > 0) {
            batch_actual = batch_remain;
        }
        int32_t batch_part_loop = (batch_actual + batch_part_len - 1) / batch_part_len;
        int32_t batch_part_remain = batch_actual % batch_part_len;
        int32_t data_actual_ldb = ldb;

        if (trans_b) {
            offset_b = batch_idx * batch_len * ldb * N + n_idx * tile_N0 * ldb;
        } else {
            if (is_complex_split_out_zN) {
                offset_b = batch_idx * batch_len * K * ROUND(ldb, tile_N0) + n_idx * K * tile_N0;
            } else {
                offset_b = batch_idx * batch_len * K * ldb + n_idx * tile_N0;
            }
        }

        auto l1_buf_b = ping_flag ? l1_base_b : l1_base_b[max_L1_B_buffer_num];
        auto event_id = ping_flag ? EVENT_ID0 : EVENT_ID1;
        auto gm_b_ptr = gm_b_tensor[offset_b];
        WAIT_FLAG(MTE1, MTE2, event_id + 2);
        if (trans_b) {
            load_matrix_zN(l1_buf_b, gm_b_tensor[offset_b], tile_N0, tile_K0, n_actual, k_round, data_actual_ldb, batch_actual,
                           ldb * N);
        } else {
            if (seq357 && step_len >= 3 && step_index == step_len - 2 &&
                is_vec_padding) {  // 倒数第二次迭代 N2 较小,直接padding成tile_N0读取
                int64_t out_batch_size = batch_size / per_N1 / per_N0;
                for (int32_t j = 0; j < batch_actual; j++) {
                    int64_t out_batch_idx = (batch_idx * batch_len + j) / (per_N1 * per_N0);
                    int64_t per_batch_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) / per_N0;
                    int64_t per_N0_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) % per_N0;
                    offset_b = (out_batch_idx * per_N0 * per_N1 + per_N0_idx * per_N1 + per_batch_idx) * K * (tile_N0);
                    load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_tensor[offset_b], tile_K0, tile_N0, k_actual, tile_N0,
                                   tile_N0);
                }
            } else if (
                seq357 && step_len >= 4 && step_index < step_len - 2 &&
                step_index !=
                    0) {  // 迭代次数大于4的其他迭代,按照padding的距离读取矩阵,布局为(2 * N1, ROUND(N2, tile_N0))
                int64_t out_batch_size = batch_size / per_N1 / per_N0;
                for (int32_t j = 0; j < batch_actual; j++) {
                    int64_t out_batch_idx = (batch_idx * batch_len + j) / (per_N1 * per_N0);
                    int64_t per_batch_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) / per_N0;
                    int64_t per_N0_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) % per_N0;
                    offset_b = (out_batch_idx * per_N0 * per_N1 + per_N0_idx * per_N1 + per_batch_idx) * K *
                                   ROUND(ldb, tile_N0) +
                               n_idx * tile_N0;
                    load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_tensor[offset_b + j * K * ROUND(ldb, tile_N0)],
                                   tile_K0, tile_N0, k_actual, tile_N0, ROUND(ldb, tile_N0));
                }
            } else if (is_complex_split_out_zN) {  // 表示虚实分离会排布成(N2 / tile_N0, 2 * N1, tile_N0)的布局方便读取
                if (k_actual == tile_K0) {
                    load_matrix_zZ(l1_buf_b, gm_b_tensor[offset_b], batch_actual * tile_K0, tile_N0, batch_actual * tile_K0, tile_N0,
                                   tile_N0);
                } else {
                    if (K / BLOCK_SIZE > batch_actual) {
                        for (int32_t j = 0; j < batch_actual; j++) {
                            load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_tensor[offset_b + j * K * ROUND(ldb, tile_N0)],
                                           tile_K0, tile_N0, k_actual, tile_N0, tile_N0);
                        }
                    } else {
                        load_matrix_zZ(l1_buf_b, gm_b_tensor[offset_b], tile_K0, tile_N0, k_actual, tile_N0, tile_N0, batch_actual,
                                       K * ROUND(ldb, tile_N0));
                    }
                }
            } else {
                if (K / BLOCK_SIZE > batch_actual) {
                    for (int32_t j = 0; j < batch_actual; j++) {
                        load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_tensor[offset_b + j * K * ldb], tile_K0, tile_N0,
                                       k_actual, tile_N0, ldb);
                    }
                } else {
                    load_matrix_zZ(l1_buf_b, gm_b_tensor[offset_b], tile_K0, tile_N0, k_actual, tile_N0, ldb, batch_actual, K * ldb);
                }
            }
        }

        SET_FLAG(MTE2, MTE1, event_id + 2);

        for (int32_t batch_part_idx = 0; batch_part_idx < batch_part_loop; batch_part_idx++) {
            int32_t batch_part_actual = batch_part_len;
            if (batch_part_idx == batch_part_loop - 1 && batch_part_remain > 0) {
                batch_part_actual = batch_part_remain;
            }
            for (int32_t n_part_idx = 0; n_part_idx < n_part_loop; n_part_idx++) {
                int32_t n0_round = (n_part_idx < n_part_loop - 1) ? n_part_len : n_round - n_part_idx * n_part_len;
                int32_t n0_actual = (n_part_idx < n_part_loop - 1) ? n_part_len : n_actual - n_part_idx * n_part_len;
                auto l0c_buf = l0c_base[l0c_ping_flag * L0C_PINGPONG_BUFFER_LEN];
                auto l0c_EVENT_ID = (l0c_ping_flag ? EVENT_ID0 : EVENT_ID1);
                WAIT_FLAG(M, MTE1, l0c_EVENT_ID);
                // *** load matrix B from L1 to L0B
                if (n_part_idx == 0 && batch_part_idx == 0) {
                    WAIT_FLAG(MTE2, MTE1, event_id + 2);
                }
                if (trans_c) {
                    l0a_buf = l0a_base[l0c_ping_flag * L0AB_PINGPONG_BUFFER_LEN];
                    auto l1_src_b = l1_buf_b[batch_part_idx * batch_part_len * tile_K0 * tile_N0 +
                                    n_part_idx * n_part_len * C0_SIZE];
                    int32_t loop;
                    int32_t l0a_stride;
                    int32_t l1_src_b_stride;
                    int32_t repeat;
                    int32_t src_stride;
                    int32_t dst_stride;

                    if (k0_round / C0_SIZE < n0_round / BLOCK_SIZE) {
                        loop = k0_round / C0_SIZE;
                        l0a_stride = CUBE_MATRIX_SIZE;
                        l1_src_b_stride = tile_N0 * C0_SIZE;
                        repeat = n0_round / BLOCK_SIZE;
                        src_stride = 1;
                        dst_stride = k0_round / C0_SIZE - 1;
                    } else {
                        loop = n0_round / BLOCK_SIZE;
                        l0a_stride = k0_round * BLOCK_SIZE;
                        l1_src_b_stride = CUBE_MATRIX_SIZE;
                        repeat = k0_round / C0_SIZE;
                        src_stride = tile_N0 / BLOCK_SIZE;
                        dst_stride = 0;
                    }
                    for (int32_t j = 0; j < batch_part_actual; j++) {
                        for (int32_t i = 0; i < loop; i++) {
                            AscendC::LoadData(
                                l0a_buf[j * tile_K0 * n_part_len + i * l0a_stride],
                                l1_src_b[j * tile_K0 * tile_N0 + i * l1_src_b_stride],
                                AscendC::LoadData2dParams(
                                    0,                   // baseIdx
                                    repeat,              // repeat
                                    src_stride,          // srcStride
                                    0,                   // sid
                                    dst_stride,          // dstGap
                                    false,               // transpose
                                    inc                  // addr_cal_mode_t
                                )
                            );
                        }
                    }
                } else {
                    l0b_buf = l0b_base[l0c_ping_flag * L0AB_PINGPONG_BUFFER_LEN];
                    if (trans_b) {
                        auto l1_src_b = l1_buf_b[batch_part_idx * batch_part_len * tile_K0 * tile_N0 +
                                        n_part_idx * n_part_len * C0_SIZE];

                        if (n0_round == tile_N0) {
                            AscendC::LoadData(
                                l0b_buf,
                                l1_src_b,
                                AscendC::LoadData2dParams(
                                    0,                   // baseIdx
                                    batch_part_actual * k0_round / C0_SIZE * n0_round / BLOCK_SIZE,  // repeat
                                    1,                   // srcStride
                                    0,                   // sid
                                    0,                   // dstGap
                                    false,               // transpose
                                    inc                  // addr_cal_mode_t
                                )
                            );
                        } else {
                            for (int32_t j = 0; j < batch_part_actual; j++) {
                                for (int32_t i = 0; i < k0_round / C0_SIZE; i++) {
                                    AscendC::LoadData(
                                        l0b_buf[j * tile_K0 * n_part_len + i * n0_round * C0_SIZE],
                                        l1_src_b[j * tile_K0 * tile_N0 + i * C0_SIZE * tile_N0],
                                        AscendC::LoadData2dParams(
                                            0,                   // baseIdx
                                            n0_round / BLOCK_SIZE,  // repeat
                                            1,                   // srcStride
                                            0,                   // sid
                                            0,                   // dstGap
                                            false,               // transpose
                                            inc                  // addr_cal_mode_t
                                        )
                                    );
                                }
                            }
                        }

                    } else {
                        auto l1_src_b = l1_buf_b[batch_part_idx * batch_part_len * tile_K0 * tile_N0 +
                                        n_part_idx * n_part_len * BLOCK_SIZE];
                        if (k0_round / BLOCK_SIZE >= n0_round / BLOCK_SIZE) {
                            for (int32_t j = 0; j < batch_part_actual; j++) {
                                for (int32_t i = 0; i < n0_round / BLOCK_SIZE; i++) {
                                    AscendC::LoadDataWithTranspose(
                                        l0b_buf[j * tile_K0 * n_part_len + i * CUBE_MATRIX_SIZE],
                                        l1_src_b[j * tile_K0 * tile_N0 + i * 2 * CUBE_MATRIX_SIZE],
                                        AscendC::LoadData2dTransposeParams(
                                            0,                             // indexID
                                            k0_round / BLOCK_SIZE,         // repeat
                                            tile_N0 / BLOCK_SIZE,               // srcStride
                                            2 * n0_round / BLOCK_SIZE - 1,  // dstGap
                                            n0_round / BLOCK_SIZE - 1,      // dstFracStride
                                            inc                           // addrmode
                                        )
                                    );
                                }
                            }
                        } else {
                            for (int32_t j = 0; j < batch_part_actual; j++) {
                                for (int32_t i = 0; i < k0_round / BLOCK_SIZE; i++) {
                                    AscendC::LoadDataWithTranspose(
                                        l0b_buf[j * tile_K0 * n_part_len + i * n0_round * BLOCK_SIZE],
                                        l1_src_b[j * tile_K0 * tile_N0 + i * tile_N0 * BLOCK_SIZE],
                                        AscendC::LoadData2dTransposeParams(
                                            0,                             // indexID
                                            n0_round / BLOCK_SIZE,         // repeat
                                            1,                             // srcStride
                                            0,                             // dstGap
                                            n0_round / BLOCK_SIZE - 1,     // dstFracStride
                                            inc                            // addrmode
                                        )
                                    );
                                }
                            }
                        }
                    }
                }

                if (n_part_idx == n_part_loop - 1 && batch_part_idx == batch_part_loop - 1) {
                    SET_FLAG(MTE1, MTE2, event_id + 2);
                }

                SET_FLAG(MTE1, M, l0c_EVENT_ID);
                WAIT_FLAG(MTE1, M, l0c_EVENT_ID);

                bool init_c = 1;
                WAIT_FLAG(FIX, M, l0c_EVENT_ID);
                for (int32_t j = 0; j < batch_part_actual; j++) {
                    if (trans_c) {
                        AscendC::MmadParams mmParam = AscendC::MmadParams(
                            n0_round,  // m
                            m0_round,  // n
                            k0_actual, // k
                            0,
                            false,
                            init_c
                        );
                        mmParam.kDirectionAlign = true;

                        AscendC::Mmad(
                            l0c_buf[j * tile_M0 * n_part_len], l0a_buf[j * tile_K0 * n_part_len], l0b_buf, mmParam
                        );
                    } else {
                        AscendC::MmadParams mmParam = AscendC::MmadParams(
                            m0_round,  // m
                            n0_actual,  // n
                            k0_actual, // k
                            0,
                            false,
                            init_c
                        );
                        mmParam.kDirectionAlign = true;

                        AscendC::Mmad(
                            l0c_buf[j * tile_M0 * n_part_len], l0a_buf, l0b_buf[j * tile_K0 * n_part_len], mmParam
                        );
                    }
                }

                SET_FLAG(M, MTE1, l0c_EVENT_ID);

                SET_FLAG(M, FIX, l0c_EVENT_ID);
                WAIT_FLAG(M, FIX, l0c_EVENT_ID);
                if (is_sync) {
                    if (n_part_idx == 0 && batch_part_idx == 0) {
                        WaitFlagDev(ping_flag + 2);
                    }
                    if (trans_c) {
                    } else {
                        // 如果 batch_len > 1 去掉矩阵的padding,便于虚实结合按照向量来操作
                        if (step_index == step_len - 1 && batch_len > 1) {
                            if (batch_part_actual > 1) {
                                uint64_t ndNum = batch_part_actual;
                                uint64_t srcNdStride = tile_M0 * n_part_len / (16 * 16);
                                uint64_t dstNdStride = tile_M0 * tile_N0;
                                uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                                AscendC::SetNdParaImpl(config);
                            }
                            int64_t offset_c = (group_id * 2 + ping_flag) * batch_len * tile_M0 * tile_N0 +
                                               (batch_part_idx * batch_part_len) * tile_M0 * tile_N0;

                            // copy from L0C to gm
                            copy_matrix_cc_to_gm(workspace + offset_c + n_part_idx * n_part_len, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                                 0,          // sid
                                                 n0_actual,  // NSize
                                                 m0_actual,  // MSize
                                                 n0_actual,  // dstStride_dst_D
                                                 m0_round,   // srcStride
                                                 0,          // UnitFlagMode
                                                 NoQuant,    // QuantPRE
                                                 0,          // ReLUPRE
                                                 false,      // channelSplit
                                                 true        // NZ2ND_EN
                            );
                        } else {
                            if (batch_part_actual > 1) {
                                uint64_t ndNum = batch_part_actual;
                                uint64_t srcNdStride = tile_M0 * n_part_len / (16 * 16);
                                uint64_t dstNdStride = tile_M0 * tile_N0;
                                uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                                AscendC::SetNdParaImpl(config);
                            }
                            int64_t offset_c = (group_id * 2 + ping_flag) * batch_len * tile_M0 * tile_N0 +
                                               (batch_part_idx * batch_part_len) * tile_M0 * tile_N0;

                            // copy from L0C to gm
                            copy_matrix_cc_to_gm(workspace + offset_c + n_part_idx * n_part_len, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                                 0,                                               // sid
                                                 (batch_part_actual > 1) ? n0_round : n0_actual,  // NSize
                                                 m0_actual,                                       // MSize
                                                 tile_N0,                                         // dstStride_dst_D
                                                 m0_round,                                        // srcStride
                                                 0,                                               // UnitFlagMode
                                                 NoQuant,                                         // QuantPRE
                                                 0,                                               // ReLUPRE
                                                 false,                                           // channelSplit
                                                 true                                             // NZ2ND_EN
                            );
                        }
                    }
                    AscendC::SetNdParaImpl(0x1);
                    if (n_part_idx == n_part_loop - 1 && batch_part_idx == batch_part_loop - 1) {
                        FftsCrossCoreSync<PIPE_FIX, 2>(ping_flag + 4);
                    }
                } else {
                    if (trans_c) {
                        if (batch_part_actual > 1) {
                            uint64_t ndNum = batch_part_actual;
                            uint64_t srcNdStride = tile_M0 * n_part_len / (16 * 16);
                            uint64_t dstNdStride = N * ldc;
                            uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                            AscendC::SetNdParaImpl(config);
                        }
                        int64_t offset_c = (batch_idx * batch_len + batch_part_idx * batch_part_len) * N * ldc +
                                           m_idx * tile_M0 + n_idx * tile_N0 * ldc;
                        // copy from L0C to gm
                        copy_matrix_cc_to_gm(gm_c + offset_c + n_part_idx * n_part_len * ldc, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                             0,          // sid
                                             m0_actual,  // NSize
                                             n0_actual,  // MSize
                                             ldc,        // dstStride_dst_D
                                             n0_round,   // srcStride
                                             0,          // UnitFlagMode
                                             NoQuant,    // QuantPRE
                                             0,          // ReLUPRE
                                             false,      // channelSplit
                                             true        // NZ2ND_EN
                        );
                    } else {
                        if (batch_part_actual > 1) {
                            uint64_t ndNum = batch_part_actual;
                            uint64_t srcNdStride = tile_M0 * n_part_len / (16 * 16);
                            uint64_t dstNdStride = M * ldc;
                            uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                            AscendC::SetNdParaImpl(config);
                        }
                        int64_t offset_c = (batch_idx * batch_len + batch_part_idx * batch_part_len) * M * ldc +
                                           m_idx * tile_M0 * ldc + n_idx * tile_N0;
                        // copy from L0C to gm
                        copy_matrix_cc_to_gm(gm_c + offset_c + n_part_idx * n_part_len, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                             0,          // sid
                                             n0_round,   // NSize
                                             m0_actual,  // MSize
                                             ldc,        // dstStride_dst_D
                                             m0_round,   // srcStride
                                             0,          // UnitFlagMode
                                             NoQuant,    // QuantPRE
                                             0,          // ReLUPRE
                                             false,      // channelSplit
                                             true        // NZ2ND_EN
                        );
                    }
                    AscendC::SetNdParaImpl(0x1);
                }
                SET_FLAG(FIX, M, l0c_EVENT_ID);
                l0c_ping_flag = 1 - l0c_ping_flag;
            }
        }

        ping_flag = 1 - ping_flag;
    }
    if (is_sync) {
        WaitFlagDev(2);
        WaitFlagDev(3);
    }

    WAIT_FLAG(M, MTE1, EVENT_ID0);
    WAIT_FLAG(M, MTE1, EVENT_ID1);
    WAIT_FLAG(M, MTE1, EVENT_ID2);
    WAIT_FLAG(M, MTE1, EVENT_ID3);

    WAIT_FLAG(MTE1, MTE2, EVENT_ID2);
    WAIT_FLAG(MTE1, MTE2, EVENT_ID3);

    WAIT_FLAG(FIX, M, EVENT_ID0);
    WAIT_FLAG(FIX, M, EVENT_ID1);
    WAIT_FLAG(FIX, M, EVENT_ID2);
    WAIT_FLAG(FIX, M, EVENT_ID3);
}

template <bool is_sync, bool trans_b, bool trans_c>
__aicore__ __inline__ __attribute__((overloadable, always_inline)) void fft_matmul_split_k_kernel(
    bool is_complex_split_out_zN, __gm__ float *__restrict__ gm_a, __gm__ float *__restrict__ gm_b,
    __gm__ float *__restrict__ gm_c, __gm__ float *__restrict__ workspace, __gm__ float *__restrict__ gm_auxil,
    __gm__ int32_t *gm_radix_list, int64_t batch_size, int64_t M, int64_t N, int64_t K, int64_t lda, int64_t ldb,
    int64_t ldc, int32_t tile_M0, int32_t tile_N0, int32_t tile_K0, int32_t step_index, int32_t step_len,
    int64_t per_N0 = 0, bool seq357 = false)
{
    if (tile_M0 == 0) {
        tile_M0 = 1;
    }
    if (tile_N0 == 0) {
        tile_N0 = 1;
    }
    if (tile_K0 == 0) {
        tile_K0 = 1;
    }
    // 当radix=3取消掉倒数第三次迭代vec的padding
    bool is_vec_padding = !((N * K / 2) % 3 == 0 && step_len >= 4);
    SET_FLAG(MTE1, MTE2, EVENT_ID2);
    SET_FLAG(MTE1, MTE2, EVENT_ID3);

    SET_FLAG(M, MTE1, EVENT_ID2);
    SET_FLAG(M, MTE1, EVENT_ID3);

    SET_FLAG(FIX, M, EVENT_ID0);
    SET_FLAG(FIX, M, EVENT_ID1);

    int64_t M_round = (M + 16 - 1) / 16 * 16;
    int64_t N_round = (N + 16 - 1) / 16 * 16;
    int64_t K_round = (K + 16 - 1) / 16 * 16;
    int64_t K_K0_round = (K + tile_K0 - 1) / tile_K0 * tile_K0;

    AscendC::GlobalTensor<float> gm_a_tensor;
    AscendC::GlobalTensor<float> gm_b_tensor;
    AscendC::GlobalTensor<float> gm_c_tensor;
    AscendC::GlobalTensor<float> workspace_tensor;
    AsdopsBuffer<ArchType::ASCEND_V220> buf;

    gm_a_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_a));
    gm_b_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_b));
    gm_c_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gm_c));
    workspace_tensor.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(workspace));

    AscendC::LocalTensor<float> l0a_base = buf.GetBuffer<BufferType::ASCEND_L0A, float>(0);
    AscendC::LocalTensor<float> l0b_base = buf.GetBuffer<BufferType::ASCEND_L0B, float>(0);
    AscendC::LocalTensor<float> l0c_base = buf.GetBuffer<BufferType::ASCEND_L0C, float>(0);
    AscendC::LocalTensor<float> l1_base_a = buf.GetBuffer<BufferType::ASCEND_CB, float>(0);
    AscendC::LocalTensor<float> l1_base_b= buf.GetBuffer<BufferType::ASCEND_CB, float>(256 * 1024);

    int32_t batch_len = L0AB_PINGPONG_BUFFER_LEN * 2 / (tile_K0 * tile_N0);
    batch_len += (batch_len == 0);

    int32_t per_N1 = -1;
    if (step_index > 0 && gm_radix_list != nullptr) {
        per_N1 = gm_radix_list[step_index - 1];
    }

    int64_t batch_remain = batch_size % batch_len;

    int64_t m_loop = (M + tile_M0 - 1) / tile_M0;
    int64_t n_loop = (N + tile_N0 - 1) / tile_N0;
    int64_t k_loop = (K + tile_K0 - 1) / tile_K0;
    int64_t batch_loop = (batch_size + batch_len - 1) / batch_len;
    if (batch_loop == 0) {
        batch_loop = 1;
    }
    int64_t loop = batch_loop * m_loop * n_loop;

    bool l0c_ping_flag = 0;
    bool ping_flag = 0;
    bool mte1_mad_ping_flag = 0;

    load_matrix_zN(l1_base_a, gm_a_tensor, M_round, K_round, M, K_round, lda);
    SET_FLAG(MTE2, MTE1, EVENT_ID7);
    WAIT_FLAG(MTE2, MTE1, EVENT_ID7);

    int16_t group_num = AscendC::GetBlockNum();
    if (group_num == 0) {
        group_num = 1;
    }
    int16_t group_id = AscendC::GetBlockIdx();

    int64_t loop_per_group;
    int64_t loop_per_group_remain;
    int64_t loop_per_group_actual;
    if (step_len > 3 && step_index == step_len - 3) {
        loop_per_group = loop / group_num;
        loop_per_group_remain = loop % group_num;
        loop_per_group_actual = loop_per_group;
        if (static_cast<int64_t>(group_id) < loop_per_group_remain) {
            loop_per_group_actual++;
        }
    } else {
        loop_per_group = (loop / m_loop) / group_num;
        loop_per_group_remain = (loop / m_loop) % group_num;
        loop_per_group_actual = loop_per_group;
        if (static_cast<int64_t>(group_id) < loop_per_group_remain) {
            loop_per_group_actual++;
        }
        loop_per_group_actual *= m_loop;
    }

    for (int64_t i = 0; i < loop_per_group_actual; i++) {
        int64_t loop_idx;
        int64_t batch_idx;
        int64_t batch_n_idx;
        int64_t m_n_idx;
        int64_t m_idx;
        int64_t n_idx;

        if (step_len > 3 && step_index == step_len - 3) {
            loop_idx =
                group_id * loop_per_group +
                (loop_per_group_remain > 0) * (group_id > loop_per_group_remain ? loop_per_group_remain : group_id) + i;
            batch_idx = loop_idx % batch_loop;
            m_n_idx = loop_idx / batch_loop;
            m_idx = m_n_idx / n_loop;
            n_idx = m_n_idx % n_loop;
        } else {
            loop_idx = group_id * loop_per_group * m_loop +
                       (loop_per_group_remain > 0) *
                           (group_id > loop_per_group_remain ? loop_per_group_remain : group_id) * m_loop +
                       i;
            m_idx = loop_idx % m_loop;
            batch_n_idx = loop_idx / m_loop;
            batch_idx = batch_n_idx % batch_loop;
            n_idx = batch_n_idx / batch_loop;
        }

        int32_t batch_actual = batch_len;
        if (batch_idx == batch_loop - 1 && batch_remain > 0) {
            batch_actual = batch_remain;
        }

        auto l0c_buf = l0c_ping_flag ? l0c_base[L0C_PINGPONG_BUFFER_LEN] : l0c_base;
        auto l0c_EVENT_ID = l0c_ping_flag ? EVENT_ID0 : EVENT_ID1;

        int64_t offset_a, offset_b;
        int32_t m_actual = (m_idx == (m_loop - 1)) ? (M - m_idx * tile_M0) : tile_M0;
        int32_t n_actual = (n_idx == (n_loop - 1)) ? (N - n_idx * tile_N0) : tile_N0;
        int32_t m_round = (m_actual + 15) / 16 * 16;
        int32_t n_round = (n_actual + 15) / 16 * 16;

        int32_t mn_max = m_round > batch_actual * n_round ? m_round : batch_actual * n_round;
        int32_t k_part_len = L0AB_PINGPONG_BUFFER_LEN / mn_max / 16 * 16;

        if (k_part_len > tile_K0 / 2 && k_part_len < tile_K0)
            k_part_len = MIN(k_part_len, ROUND(tile_K0 / 2, 16));

        for (int32_t k_idx = 0; k_idx < (int32_t)k_loop; k_idx++) {
            if (trans_b) {
                offset_b = batch_idx * batch_len * ldb * N + n_idx * tile_N0 * ldb + k_idx * tile_K0;
            } else {
                if (is_complex_split_out_zN) {
                    offset_b = batch_idx * batch_len * K * ROUND(ldb, tile_N0) + k_idx * tile_K0 * tile_N0 +
                               n_idx * K * tile_N0;
                } else {
                    offset_b = batch_idx * batch_len * K * ldb + k_idx * tile_K0 * ldb + n_idx * tile_N0;
                }
            }

            int32_t k_actual = (k_idx == (k_loop - 1)) ? (K - k_idx * tile_K0) : tile_K0;
            int32_t k_round = (k_actual + 15) / 16 * 16;
            int32_t k_part_loop = (k_actual + k_part_len - 1) / k_part_len;

            auto l1_buf_a = l1_base_a;
            auto l1_buf_b = ping_flag ? l1_base_b : l1_base_b[L1_PINGPONG_BUFFER_LEN / 2];
            if (k_loop > 1) {
                l1_buf_b = l1_base_b[k_idx * batch_len * tile_K0 * tile_N0];
            }
            auto event_id = ping_flag ? EVENT_ID0 : EVENT_ID1;
            auto gm_b_ptr = gm_b_tensor[offset_b];
            // *** load matrix B to L1
            WAIT_FLAG(MTE1, MTE2, event_id + 2);
            if (m_idx == 0 || (step_len > 3 && step_index == step_len - 3)) {
                if (trans_b) {
                    load_matrix_zN(l1_buf_b, gm_b_tensor[offset_b], tile_N0, tile_K0, n_actual, k_round, ldb, batch_actual, ldb * N);
                } else {
                    if (seq357 && step_len >= 3 && step_index == step_len - 2 &&
                        is_vec_padding) {  // 倒数第二次迭代 N2 较小,直接padding成tile_N0读取
                        int64_t out_batch_size = batch_size / per_N1 / per_N0;
                        for (int32_t j = batch_actual - 1; j >= 0; j--) {
                            int64_t out_batch_idx = (batch_idx * batch_len + j) / (per_N1 * per_N0);
                            int64_t per_batch_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) / per_N0;
                            int64_t per_N0_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) % per_N0;
                            offset_b = (out_batch_idx * per_N0 * per_N1 + per_N0_idx * per_N1 + per_batch_idx) * K *
                                           (tile_N0) +
                                       k_idx * tile_K0 * (tile_N0);
                            auto gm_b_ptr = gm_b_tensor[offset_b];
                            load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_ptr, tile_K0, tile_N0, k_actual,
                                           tile_N0, tile_N0);
                        }
                    } else if (
                        seq357 && step_len >= 4 && step_index < step_len - 2 &&
                        step_index !=
                            0) {  // 迭代次数大于4的其他迭代,按照padding的距离读取矩阵,布局为(2 * N1, ROUND(N2, tile_N0))
                        int64_t out_batch_size = batch_size / per_N1 / per_N0;
                        for (int32_t j = batch_actual - 1; j >= 0; j--) {
                            int64_t out_batch_idx = (batch_idx * batch_len + j) / (per_N1 * per_N0);
                            int64_t per_batch_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) / per_N0;
                            int64_t per_N0_idx = (batch_idx * batch_len + j) % (per_N1 * per_N0) % per_N0;
                            offset_b = (out_batch_idx * per_N0 * per_N1 + per_N0_idx * per_N1 + per_batch_idx) * K *
                                           ROUND(ldb, tile_N0) +
                                       k_idx * tile_K0 * ROUND(ldb, tile_N0) + n_idx * tile_N0;
                            auto gm_b_ptr = gm_b_tensor[offset_b];
                            load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_ptr[j * K * ROUND(ldb, tile_N0)],
                                           tile_K0, tile_N0, k_actual, tile_N0, ROUND(ldb, tile_N0));
                        }
                    } else if (
                        is_complex_split_out_zN) {  // 表示虚实分离会排布成(N2 / tile_N0, 2 * N1, tile_N0)的布局方便读取
                        if (k_actual == tile_K0) {
                            load_matrix_zZ(l1_buf_b, gm_b_ptr, batch_actual * tile_K0, tile_N0, batch_actual * tile_K0,
                                           tile_N0, tile_N0);
                        } else {
                            for (int32_t j = batch_actual - 1; j >= 0; j--) {
                                load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_ptr[j * K * ROUND(ldb, tile_N0)],
                                               tile_K0, tile_N0, k_actual, tile_N0, tile_N0);
                            }
                        }
                    } else {
                        if (K / BLOCK_SIZE > batch_actual) {
                            for (int32_t j = batch_actual - 1; j >= 0; j--) {
                                load_matrix_zZ(l1_buf_b[j * tile_K0 * tile_N0], gm_b_ptr[j * K * ldb], tile_K0,
                                               tile_N0, k_actual, tile_N0, ldb);
                            }
                        } else {
                            load_matrix_zZ(l1_buf_b, gm_b_ptr, tile_K0, tile_N0, k_actual, tile_N0, ldb, batch_actual,
                                           K * ldb);
                        }
                    }
                }
            }

            SET_FLAG(MTE2, MTE1, event_id + 2);

            for (int32_t k_part_idx = 0; k_part_idx < k_part_loop; k_part_idx++) {
                int32_t k0_round = (k_part_idx < k_part_loop - 1) ? k_part_len : k_round - k_part_idx * k_part_len;
                int32_t k0_actual = (k_part_idx < k_part_loop - 1) ? k_part_len : k_actual - k_part_idx * k_part_len;

                auto mte1_mad_event_id = mte1_mad_ping_flag ? EVENT_ID2 : EVENT_ID3;
                auto l0a_buf = l0a_base[mte1_mad_ping_flag * L0AB_PINGPONG_BUFFER_LEN];
                auto l0b_buf = l0b_base[mte1_mad_ping_flag * L0AB_PINGPONG_BUFFER_LEN];

                WAIT_FLAG(M, MTE1, mte1_mad_event_id);
                if (trans_c) {
                    auto l1_src_a =
                        l1_buf_a[m_idx * tile_M0 * C0_SIZE + (k_idx * tile_K0 + k_part_idx * k_part_len) * M_round];
                    for (int32_t i = 0; i < k0_round / C0_SIZE; i++) {
                        AscendC::LoadData(
                            l0b_buf[i * m_round * C0_SIZE],
                            l1_src_a[i * C0_SIZE * M_round],
                            AscendC::LoadData2dParams(
                                0,                   // baseIdx
                                m_round / BLOCK_SIZE,  // repeat
                                1,                    // srcStride
                                0,                   // sid
                                0,                   // dstGap
                                false,               // transpose
                                inc                  // addr_cal_mode_t
                            )
                        );
                    }
                } else {
                    auto l1_src_a =
                        l1_buf_a[m_idx * tile_M0 * C0_SIZE + (k_idx * tile_K0 + k_part_idx * k_part_len) * M_round];
                    for (int32_t i = 0; i < m_round / BLOCK_SIZE; i++) {
                        AscendC::LoadData(
                            l0a_buf[i * k0_round * BLOCK_SIZE],
                            l1_src_a[i * CUBE_MATRIX_SIZE],
                            AscendC::LoadData2dParams(
                                0,                   // baseIdx
                                k0_round / C0_SIZE,  // repeat
                                M_round / BLOCK_SIZE,  // srcStride
                                0,                   // sid
                                0,                   // dstGap
                                false,               // transpose
                                inc                  // addr_cal_mode_t
                            )
                        );
                    }
                }
                // *** load matrix B from L1 to L0B
                if (k_part_idx == 0) {
                    WAIT_FLAG(MTE2, MTE1, event_id + 2);
                }
                if (trans_c) {
                    auto l1_src_b = l1_buf_b[k_part_idx * k_part_len * tile_N0];
                    int32_t loop;
                    int32_t l0a_stride;
                    int32_t l1_src_b_stride;
                    int32_t repeat;
                    int32_t src_stride;
                    int32_t dst_stride;

                    if (k0_round / C0_SIZE < n_round / BLOCK_SIZE) {
                        loop = k0_round / C0_SIZE;
                        l0a_stride = CUBE_MATRIX_SIZE;
                        l1_src_b_stride = tile_N0 * C0_SIZE;
                        repeat = n_round / BLOCK_SIZE;
                        src_stride = 1;
                        dst_stride = k0_round / C0_SIZE - 1;
                    } else {
                        loop = n_round / BLOCK_SIZE;
                        l0a_stride = k0_round * BLOCK_SIZE;
                        l1_src_b_stride = CUBE_MATRIX_SIZE;
                        repeat = k0_round / C0_SIZE;
                        src_stride = tile_N0 / BLOCK_SIZE;
                        dst_stride = 0;
                    }
                    for (int32_t j = 0; j < batch_actual; j++) {
                        for (int32_t i = 0; i < loop; i++) {
                            AscendC::LoadData(
                                l0a_buf[j * k0_round * n_round + i * l0a_stride],
                                l1_src_b[j * tile_K0 * tile_N0 + i * l1_src_b_stride],
                                AscendC::LoadData2dParams(
                                    0,                   // baseIdx
                                    repeat,  // repeat
                                    src_stride,  // srcStride
                                    0,                   // sid
                                    dst_stride,                   // dstGap
                                    false,               // transpose
                                    inc                  // addr_cal_mode_t
                                )
                            );
                        }
                    }
                } else {
                    if (trans_b) {
                        auto l1_src_b = l1_buf_b[k_part_idx * k_part_len * tile_N0];
                        if (n_round == tile_N0) {
                            for (int32_t j = 0; j < batch_actual; j++) {
                                AscendC::LoadData(
                                    l0b_buf[j * k0_round * n_round],
                                    l1_src_b[j * tile_K0 * tile_N0],
                                    AscendC::LoadData2dParams(
                                        0,                   // baseIdx
                                        k0_round / C0_SIZE * n_round / BLOCK_SIZE,  // repeat
                                        1,  // srcStride
                                        0,                   // sid
                                        0,                   // dstGap
                                        false,               // transpose
                                        inc                  // addr_cal_mode_t
                                    )
                                );
                            }
                        } else {
                            for (int32_t j = 0; j < batch_actual; j++) {
                                for (int32_t i = 0; i < k0_round / C0_SIZE; i++) {
                                    AscendC::LoadData(
                                        l0b_buf[j * k0_round * n_round + i * n_round * C0_SIZE],
                                        l1_src_b[j * tile_K0 * tile_N0 + i * C0_SIZE * tile_N0],
                                        AscendC::LoadData2dParams(
                                            0,                   // baseIdx
                                            n_round / BLOCK_SIZE,  // repeat
                                            1,  // srcStride
                                            0,                   // sid
                                            0,                   // dstGap
                                            false,               // transpose
                                            inc                  // addr_cal_mode_t
                                        )
                                    );
                                }
                            }
                        }
                    } else {
                        auto l1_src_b = l1_buf_b[k_part_idx * k_part_len * tile_N0];
                        for (int32_t j = 0; j < batch_actual; j++) {
                            for (int32_t i = 0; i < (int32_t)(n_round / BLOCK_SIZE); i++) {
                                AscendC::LoadDataWithTranspose(
                                    l0b_buf[j * k0_round * n_round + i * CUBE_MATRIX_SIZE],
                                    l1_src_b[j * tile_K0 * tile_N0 + i * 2 * CUBE_MATRIX_SIZE],
                                    AscendC::LoadData2dTransposeParams(
                                        0,                             // indexID
                                        k0_round / BLOCK_SIZE,         // repeat
                                        tile_N0 / BLOCK_SIZE,               // srcStride
                                        2 * n_round / BLOCK_SIZE - 1,  // dstGap
                                        n_round / BLOCK_SIZE - 1,      // dstFracStride
                                        inc                           // addrmode
                                    )
                                );
                            }
                        }
                    }
                }
                if (k_part_idx == k_part_loop - 1) {
                    SET_FLAG(MTE1, MTE2, event_id + 2);
                }

                SET_FLAG(MTE1, M, mte1_mad_event_id);
                WAIT_FLAG(MTE1, M, mte1_mad_event_id);

                bool init_c = (k_idx == 0 && k_part_idx == 0);
                if (init_c) {
                    WAIT_FLAG(FIX, M, l0c_EVENT_ID);
                }
                for (int32_t j = 0; j < batch_actual; j++) {
                    if (trans_c) {
                        AscendC::MmadParams mmParam = AscendC::MmadParams(
                            n_round,  // m
                            m_round,  // n
                            k0_actual, // k
                            0,
                            false,
                            init_c
                        );
                        mmParam.kDirectionAlign = true;

                        AscendC::Mmad(
                            l0c_buf[j * m_round * n_round], l0a_buf[j * k0_round * n_round], l0b_buf, mmParam
                        );
                    } else {
                        AscendC::MmadParams mmParam = AscendC::MmadParams(
                            m_round,  // m
                            n_actual,  // n
                            k0_actual, // k
                            0,
                            false,
                            init_c
                        );
                        mmParam.kDirectionAlign = true;

                        AscendC::Mmad(
                            l0c_buf[j * m_round * n_round], l0a_buf, l0b_buf[j * k0_round * n_round], mmParam
                        );
                    }
                }

                PIPE_BARRIER(M);
                SET_FLAG(M, MTE1, mte1_mad_event_id);
                mte1_mad_ping_flag = 1 - mte1_mad_ping_flag;
            }
            ping_flag = 1 - ping_flag;
        }
        SET_FLAG(M, FIX, l0c_EVENT_ID);
        WAIT_FLAG(M, FIX, l0c_EVENT_ID);

        if (is_sync) {
            WaitFlagDev(l0c_ping_flag + 2);
            if (trans_c) {
            } else {
                // 如果 L0AB_PINGPONG_BUFFER_LEN / (tile_K0 * tile_N0) >= 1 去掉矩阵的padding,便于虚实结合按照向量来操作
                if (step_index == step_len - 1 && L0AB_PINGPONG_BUFFER_LEN / (tile_K0 * tile_N0) >= 1) {
                    if (batch_actual > 1) {
                        uint64_t ndNum = batch_actual;
                        uint64_t srcNdStride = m_round * n_round / (16 * 16);
                        uint64_t dstNdStride = tile_M0 * tile_N0;
                        uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                        AscendC::SetNdParaImpl(config);
                    }
                    int64_t offset_c = (group_id * 2 + l0c_ping_flag) * batch_len * tile_M0 * tile_N0;

                    // copy from L0C to gm
                    copy_matrix_cc_to_gm(workspace + offset_c, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                         0,         // sid
                                         n_actual,  // NSize
                                         m_actual,  // MSize
                                         n_actual,  // dstStride_dst_D
                                         m_round,   // srcStride
                                         0,         // UnitFlagMode
                                         NoQuant,   // QuantPRE
                                         0,         // ReLUPRE
                                         false,     // channelSplit
                                         true       // NZ2ND_EN
                    );
                } else {
                    if (batch_actual > 1) {
                        uint64_t ndNum = batch_actual;
                        uint64_t srcNdStride = m_round * n_round / (16 * 16);
                        uint64_t dstNdStride = tile_M0 * tile_N0;
                        uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                        AscendC::SetNdParaImpl(config);
                    }
                    int64_t offset_c = (group_id * 2 + l0c_ping_flag) * batch_len * tile_M0 * tile_N0;

                    // copy from L0C to gm
                    copy_matrix_cc_to_gm(workspace + offset_c, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                         0,         // sid
                                         tile_N0,   // NSize
                                         m_actual,  // MSize
                                         tile_N0,   // dstStride_dst_D
                                         m_round,   // srcStride
                                         0,         // UnitFlagMode
                                         NoQuant,   // QuantPRE
                                         0,         // ReLUPRE
                                         false,     // channelSplit
                                         true       // NZ2ND_EN
                    );
                }
            }
            AscendC::SetNdParaImpl(0x1);
            FftsCrossCoreSync<PIPE_FIX, 2>(l0c_ping_flag + 4);
        } else {
            if (trans_c) {
                if (batch_actual > 1) {
                    uint64_t ndNum = batch_actual;
                    uint64_t srcNdStride = m_round * n_round / (16 * 16);
                    uint64_t dstNdStride = N * ldc;
                    uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                    AscendC::SetNdParaImpl(config);
                }
                int64_t offset_c = batch_idx * batch_len * N * ldc + m_idx * tile_M0 + n_idx * tile_N0 * ldc;
                // copy from L0C to gm
                copy_matrix_cc_to_gm(gm_c + offset_c, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                     0,         // sid
                                     m_actual,  // NSize
                                     n_actual,  // MSize
                                     ldc,       // dstStride_dst_D
                                     n_round,   // srcStride
                                     0,         // UnitFlagMode
                                     NoQuant,   // QuantPRE
                                     0,         // ReLUPRE
                                     false,     // channelSplit
                                     true       // NZ2ND_EN
                );
            } else {
                if (batch_actual > 1) {
                    uint64_t ndNum = batch_actual;
                    uint64_t srcNdStride = m_round * n_round / (16 * 16);
                    uint64_t dstNdStride = M * ldc;
                    uint64_t config = ndNum | (srcNdStride << 16) | (dstNdStride << 32);
                    AscendC::SetNdParaImpl(config);
                }
                int64_t offset_c = batch_idx * batch_len * M * ldc + m_idx * tile_M0 * ldc + n_idx * tile_N0;
                // copy from L0C to gm
                copy_matrix_cc_to_gm(gm_c + offset_c, reinterpret_cast<__cc__ float *>((uintptr_t)(l0c_buf.GetPhyAddr())),
                                     0,         // sid
                                     n_actual,  // NSize
                                     m_actual,  // MSize
                                     ldc,       // dstStride_dst_D
                                     m_round,   // srcStride
                                     0,         // UnitFlagMode
                                     NoQuant,   // QuantPRE
                                     0,         // ReLUPRE
                                     false,     // channelSplit
                                     true       // NZ2ND_EN
                );
            }
        }
        AscendC::SetNdParaImpl(0x1);
        l0c_ping_flag = 1 - l0c_ping_flag;

        SET_FLAG(FIX, M, l0c_EVENT_ID);
    }
    if (is_sync) {
        WaitFlagDev(2);
        WaitFlagDev(3);
    }

    WAIT_FLAG(MTE1, MTE2, EVENT_ID2);
    WAIT_FLAG(MTE1, MTE2, EVENT_ID3);

    WAIT_FLAG(M, MTE1, EVENT_ID2);
    WAIT_FLAG(M, MTE1, EVENT_ID3);

    WAIT_FLAG(FIX, M, EVENT_ID0);
    WAIT_FLAG(FIX, M, EVENT_ID1);
}