#include "ucp_csu.h"
#include "ape_csu.h"
#include "ucp_drv_common.h"
#include "ucp_utility.h"


/*!
* @brief:  CSU init for ape0 and ape1
*  		CSU初始化，只在APE0调用即可，APE1若要使用CSU，先在APE0初始化
*  		如果APE0没有启动，只启动了APE1，也可在APE1调用该接口，完成初始化，再调用CSU的其他接口
* @author:  xinxin.li
* @Date:  2022年6月1日
*/
void ape_csu_init()
{
    int32_t coreId = get_core_id();
    int32_t apeId = coreId & 0x1;
	int32_t i;
	
	for(i = 0; i < APE_DMA_REG_NUM; i++)
	{
		do_write(((uint32_t*)(&APC_CSU_DMAYNUMXNUM0) + (apeId<<6) + (i<<3)), ((4 << 16) | 1024));
		do_write(((uint32_t*)(&APC_CSU_DMAYSTEPL0) + (apeId<<6) + (i<<3)), 1024);
		do_write(((uint32_t*)(&APC_CSU_DMAYSTEPH0) + (apeId<<6) + (i<<3)), 0);
		do_write(((uint32_t*)(&APC_CSU_DMAZSTEPL0) + (apeId<<6) + (i<<3)), 4096);
		do_write(((uint32_t*)(&APC_CSU_DMAZSTEPH0) + (apeId<<6) + (i<<3)), 0);
	}
    if (8 > coreId)
    {
		APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x2A<<16) + (0x5<<5) + 0x9;
		APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x2A<<16) + (0x1<<14) + (0x5<<5) + 0x9; //smsel=0x2A, burst sel = 0
		APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x2A<<16) + (0x2<<14) + (0x5<<5) + 0x9;
		APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x2A<<16) + (0x3<<14) + (0x5<<5) + 0x9;
    }
    else
    {
        APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x4<<5) + 0x7;
        APC_CSU_EM_BS_SMSEL_PREDATANUM = (0x1<<14) + (0x4<<5) + 0x7; //smsel=0x0, burst sel = 0
    }
	do_write(((uint32_t*)(&APC_CSU_ALMOSTFULLSENDTHRED)), 0); // ??

	APC_CSU_FINDDMATAG = 0x60; // DMA搬移完成才能查到结束状态
	APC_CSU_TAGMASK = 0xFFFFFFFF;
	
	return;
}

//tag			0			8			16			24		reg_group = 0(0->1)		CmdFIFO = 	0
//tag			1			9			17			25					1(2->3)					1
//tag			2			10			18			26					2(4->5)					2
//tag			3			11			19			27					3(6->7)					3
//tag			4			12			20			28					4(8->9)					0
//tag			5			13			21			29					5(10->11)				1	
//tag			6			14			22			30					6(12->13)				2	
//tag			7			15			23			31					7(14->15)				3	

// get unused register group according to tag number
int32_t get_free_reg_group(uint8_t tag)
{
	if (31 < tag)
	{
		return -1;
	}

    int32_t coreId = get_core_id();
	uint8_t fifoNum = 0;
	if (8 <= coreId)
	{
		fifoNum = tag & 0x1;	// 0~1
	}
	else
	{
		fifoNum = tag & 0x3;	// 0~3
	}
	uint8_t regGroup = tag & 0x7;	// 0~7
	uint32_t bitMap = (0x01010101<<regGroup);
	uint32_t dmaStatus = do_read_volatile(&APC_CSU_DMASTATUS);
	if (bitMap != (dmaStatus & bitMap))
	{
		return -1;
	}

	return ((fifoNum<<16) | regGroup);	// [31:16]fifoNum(0~3), [15:0]reg group(0~7)
}

/*!
* @brief:  查看tag号对应的dma任务是否完成，未完成返回0，完成返回1
*  		   如果isWait==1，则等待该tag对应的任务完成，再返回，返回1
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: task_tag :         [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @return: 1：已完成，0：未完成
*/
int32_t ape_csu_task_lookup(uint8_t task_tag, uint8_t isWait)
{
	if (0 == isWait)
	{
		return (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << task_tag));
	}
	else
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << task_tag)));
		return 1;
	}
}

/*!
* @brief:  一维搬移到一维，使用寄存器组0和组1实现L2G的Dma搬移
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
int32_t ape_csu_dma_1D_L2G_ch0ch1_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
//	UINT8 regGroup = 0; // reg0 and reg1 for L2G 1D transfer
//	UINT8 fifoNum = 0;	// FIFO0 for L2G transfer
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	uint32_t offset_w = (apeId<<6);

	// src reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen); // DM,CGran=1,Gran=6
	// dst reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL1) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH1) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM1) + offset_w), dataLen); // write global,size=0

	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 0 + (apeId<<3);
	dmaCmdL.idDst = 1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1)), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}

/*!
* @brief:  一维搬移到一维，使用寄存器组2和组3实现L2G的Dma搬移
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
int32_t ape_csu_dma_1D_L2G_ch2ch3_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
//	UINT8 regGroup = 0; // reg0 and reg1 for L2G 1D transfer
//	UINT8 fifoNum = 0;	// FIFO0 for L2G transfer
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	uint32_t offset_w = (apeId<<6);

	// src reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL2) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH2) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM2) + offset_w), (0xE<<24) | dataLen); // DM,size=0,CGran=1,Gran=6
	// dst reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL3) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH3) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM3) + offset_w), dataLen); // write global,size=0
	
	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 2 + (apeId<<3);
	dmaCmdL.idDst = 3 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1)), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}
					
/*!
* @brief:  一维搬移到一维，使用寄存器组0和组1实现G2L的Dma搬移
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
int32_t ape_csu_dma_1D_G2L_ch0ch1_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
//	UINT8 regGroup = 1; // reg2 and reg3 for G2L or G2G 1D transfer
//	UINT8 fifoNum = 1;	// FIFO1 for G2L or G2G transfer
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	uint32_t offset_w = (apeId<<6);

	// src reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xF<<28) | dataLen); // read global,size=0xF
	// dst reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL1) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH1) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM1) + offset_w), (0xE<<24) | dataLen); // write global or dm, cgran=1,gran=6,only valid for dm

	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 0 + (apeId<<3);
	dmaCmdL.idDst = 1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO1) + (fifoId<<1)), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}
					
/*!
* @brief:  一维搬移到一维，使用寄存器组2和组3实现G2L的Dma搬移
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
int32_t ape_csu_dma_1D_G2L_ch2ch3_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
//	UINT8 regGroup = 1; // reg2 and reg3 for G2L or G2G 1D transfer
//	UINT8 fifoNum = 1;	// FIFO1 for G2L or G2G transfer
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	uint32_t offset_w = (apeId<<6);

	// src reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL2) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH2) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM2) + offset_w), (0xF<<28) | dataLen); // read global,size=0xF
	// dst reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL3) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH3) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM3) + offset_w), (0xE<<24) | dataLen); // write global or dm, cgran=1,gran=6,only valid for dm

	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 2 + (apeId<<3);
	dmaCmdL.idDst = 3 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO1) + (fifoId<<1)), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}

/*!
* @brief:  二维搬移到二维
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: blockLenSrc :      [源第一维度搬移字节数 ]
* @param: blockStepSrc :     [源第二维度搬移步进字节数 ]
* @param: addrDst :          [目的地址 ]
* @param: blockLenDst :      [目的第一维度搬移字节数 ]
* @param: blockStepDst :     [目的第二维度搬移步进字节数 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @param: regGroup :         [寄存器组，2：源寄存器组4，目的寄存器组5；3：源寄存器组6，目的寄存器组7 ]
* @param: dir :           	 [0：L2G，1：G2L or    G2G ]
*/
int32_t ape_csu_dma_2Dto2D_transfer(uint64_t addrSrc, uint16_t blockLenSrc, uint64_t blockStepSrc,  
								uint64_t addrDst, uint16_t blockLenDst, uint64_t blockStepDst, 
								uint32_t dataLen, uint8_t tag, uint8_t isWait, uint8_t regGroup, uint8_t dir)
{
	if ((DMA_REG_GROUP2 > regGroup) || (DMA_REG_GROUP3 < regGroup))
	{
		return -1;
	}
	if ((0 != dir) && (1 != dir))
	{
		return -1;
	}
	
	int32_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	// src
	uint32_t offset_w = (apeId<<3) + (regGroup<<1);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), blockStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(blockStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), blockStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(blockStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (1<<16)|blockLenSrc);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xF<<28) | dataLen);
	}

	// dst
	offset_w = (apeId<<3) + (regGroup<<1) + 1;
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), blockStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(blockStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), blockStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(blockStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (1<<16)|blockLenDst);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}

	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = (regGroup<<1) + (apeId<<3);
	dmaCmdL.idDst = (regGroup<<1)+1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1) + dir), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0; 
}

/*!
* @brief:  三维搬移到一维
* @author:  xinxin.li
* @Date:  2022年6月1日
* @param: addrSrc :          [源地址 ]
* @param: xNumSrc :          [源第一维度搬移字节数 ]
* @param: yNumSrc :          [源第二维度搬移个数 ]
* @param: yStepSrc :         [源第二维度搬移步进字节数 ]
* @param: zStepSrc :         [源第三维度搬移步进字节数 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @param: regGroup :         [寄存器组，2：源寄存器组4，目的寄存器组5；3：源寄存器组6，目的寄存器组7 ]
* @param: dir :           	 [0：L2G，1：G2L or    G2G ]
*/
int32_t ape_csu_dma_3Dto1D_transfer(uint64_t addrSrc, uint16_t xNumSrc, uint16_t yNumSrc, uint64_t yStepSrc, uint64_t zStepSrc, 
								uint64_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait, uint8_t regGroup, uint8_t dir)
{
	if ((DMA_REG_GROUP2 > regGroup) || (DMA_REG_GROUP3 < regGroup))
	{
		return -1;
	}
	if ((0 != dir) && (1 != dir))
	{
		return -1;
	}
	
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	// src
	uint32_t offset_w = (apeId<<3) + (regGroup<<1);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), yStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(yStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), zStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(zStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (yNumSrc<<16)|xNumSrc);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xF<<28) | dataLen);
	}
	// dst
	offset_w = (apeId<<3) + (regGroup<<1) + 1;
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), 256);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), 0);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), 1024);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), 0);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (4<<16)|256);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}

	stCsuDmaCmdL dmaCmdL;
    *((uint32_t *)(&dmaCmdL)) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = (regGroup<<1) + (apeId<<3);
	dmaCmdL.idDst = (regGroup<<1)+1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1) + dir), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}

/*!
* @brief:  一维搬移到三维
* @author:  xinxin.li
* @Date:  2023年8月28日
* @param: addrSrc :          [源地址 ]
* @param: addrDst :          [目的地址 ]
* @param: xNumDst :          [目的第一维度搬移字节数 ]
* @param: yNumDst :          [目的第二维度搬移个数 ]
* @param: yStepDst :         [目的第二维度搬移步进字节数 ]
* @param: zStepDst :         [目的第三维度搬移步进字节数 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @param: regGroup :         [寄存器组，2：源寄存器组4，目的寄存器组5；3：源寄存器组6，目的寄存器组7 ]
* @param: dir :           	 [0：L2G，1：G2L or    G2G ]
*/
int32_t ape_csu_dma_1Dto3D_transfer(uint64_t addrSrc, uint64_t addrDst, uint16_t xNumDst, uint16_t yNumDst, uint64_t yStepDst, uint64_t zStepDst, 
								uint32_t dataLen, uint8_t tag, uint8_t isWait, uint8_t regGroup, uint8_t dir)
{
	if ((DMA_REG_GROUP2 > regGroup) || (DMA_REG_GROUP3 < regGroup))
	{
		return -1;
	}
	if ((0 != dir) && (1 != dir))
	{
		return -1;
	}
	
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	// src
	uint32_t offset_w = (apeId<<3) + (regGroup<<1);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), 256);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), 0);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), 1024);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), 0);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (4<<16)|256);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xF<<28) | dataLen);
	}
	// dst
	offset_w = (apeId<<3) + (regGroup<<1) + 1;
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), yStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(yStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), zStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(zStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (yNumDst<<16)|xNumDst);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}

	stCsuDmaCmdL dmaCmdL;
    *((uint32_t *)(&dmaCmdL)) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = (regGroup<<1) + (apeId<<3);
	dmaCmdL.idDst = (regGroup<<1)+1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1) + dir), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}

/*!
* @brief:  三维搬移到三维
* @author:  xinxin.li
* @Date:  2023年8月28日
* @param: addrSrc :          [源地址 ]
* @param: xNumSrc :          [源第一维度搬移字节数 ]
* @param: yNumSrc :          [源第二维度搬移个数 ]
* @param: yStepSrc :         [源第二维度搬移步进字节数 ]
* @param: zStepSrc :         [源第三维度搬移步进字节数 ]
* @param: addrDst :          [目的地址 ]
* @param: xNumDst :          [目的第一维度搬移字节数 ]
* @param: yNumDst :          [目的第二维度搬移个数 ]
* @param: yStepDst :         [目的第二维度搬移步进字节数 ]
* @param: zStepDst :         [目的第三维度搬移步进字节数 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
* @param: regGroup :         [寄存器组，2：源寄存器组4，目的寄存器组5；3：源寄存器组6，目的寄存器组7 ]
* @param: dir :           	 [0：L2G，1：G2L or    G2G ]
*/
int32_t ape_csu_dma_3Dto3D_transfer(uint64_t addrSrc, uint16_t xNumSrc, uint16_t yNumSrc, uint64_t yStepSrc, uint64_t zStepSrc, 
								uint64_t addrDst, uint16_t xNumDst, uint16_t yNumDst, uint64_t yStepDst, uint64_t zStepDst, 
								uint32_t dataLen, uint8_t tag, uint8_t isWait, uint8_t regGroup, uint8_t dir)
{
	if ((DMA_REG_GROUP2 > regGroup) || (DMA_REG_GROUP3 < regGroup))
	{
		return -1;
	}
	if ((0 != dir) && (1 != dir))
	{
		return -1;
	}
	
	uint8_t apeId = get_core_id();
	uint8_t fifoId = (8 <= apeId) ? (0) : (apeId & 0x1);
	apeId = apeId & 0x1;
	// src
	uint32_t offset_w = (apeId<<3) + (regGroup<<1);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), yStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(yStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), zStepSrc & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(zStepSrc >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (yNumSrc<<16)|xNumSrc);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xF<<28) | dataLen);
	}
	// dst
	offset_w = (apeId<<3) + (regGroup<<1) + 1;
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRL0) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPL0) + offset_w), yStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYSTEPH0) + offset_w), (uint32_t)(yStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPL0) + offset_w), zStepDst & 0xFFFFFFFF);
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAZSTEPH0) + offset_w), (uint32_t)(zStepDst >> 32));
	do_write(((stCsuDmaReg*)(&APC_CSU_DMAYNUMXNUM0) + offset_w), (yNumDst<<16)|xNumDst);
	if (0 == dir) // L2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), dataLen);
	}
	else // G2L or G2G
	{
		do_write(((stCsuDmaReg*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), (0xE<<24) | dataLen);
	}

	stCsuDmaCmdL dmaCmdL;
    *((uint32_t *)(&dmaCmdL)) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = (regGroup<<1) + (apeId<<3);
	dmaCmdL.idDst = (regGroup<<1)+1 + (apeId<<3);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + (fifoId<<1) + dir), (*(uint32_t*)(&dmaCmdL)));

	if(1 == isWait)
	{
		while (0 == (do_read_volatile((uint32_t*)(&APC_CSU_DMASTATUS)) & (1 << tag)));
	}
	
	return 0;
}


//pucch freq data copy from sm to dm
/*!
* @brief:  三维外部搬移到一维内部，外部地址不超过32位，寄存器组源用4目的用0
* @author:  chunmeng.li
* @Date:  2022年6月13日
* @param: addrSrc :          [源地址 ]
* @param: xNumSrc :          [源第一维度搬移字节数 ]
* @param: yNumSrc :          [源第二维度搬移个数 ]
* @param: yStepSrc :         [源第二维度搬移步进字节数 ]
* @param: zStepSrc :         [源第三维度搬移步进字节数 ]
* @param: addrDst :          [目的地址 ]
* @param: dataLen :          [搬移总字节数 ]
* @param: tag :              [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :           [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
void ape_csu_dma_3Dto1D_G2L_ch4ch0_simp_transfer(uint32_t addrSrc, uint16_t xNumSrc, uint16_t yNumSrc, 
    uint32_t yStepSrc, uint32_t zStepSrc,  uint32_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
   uint32_t cmdInitValue;
   //bufferOffset为链表端bufferID寄存器偏移word数（每个bufferId占8个word）
   uint32_t bufferOffset = (get_core_id() & 1) << 6;
   uint32_t cmdFifoId = (bufferOffset >> 5) | 1;
   //1.配置源端寄存器
   do_write((void *)(&APC_CSU_DMAADDRL4 + bufferOffset), addrSrc);
   do_write((void *)(&APC_CSU_DMAADDRH4 + bufferOffset), 0);
   do_write((void *)(&APC_CSU_DMAYSTEPL4 + bufferOffset), yStepSrc);
   do_write((void *)(&APC_CSU_DMAZSTEPL4 + bufferOffset), zStepSrc);
   do_write((void *)(&APC_CSU_DMAYNUMXNUM4 + bufferOffset), (yNumSrc << 16) | xNumSrc);
   do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM4 + bufferOffset), 0xF0000000 | dataLen);
   //2.配置目的端寄存器
   do_write((void *)(&APC_CSU_DMAADDRL0 + bufferOffset), addrDst);
   do_write((void *)(&APC_CSU_DMAADDRH0 + bufferOffset), 0);
   do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM0 + bufferOffset), 0x0E000000 | dataLen);
   //3.配置命令字
   bufferOffset <<= 5;
   //readBufferId为(bufferOffset>>8) + 4
   //writeBufferId为(bufferOffset>>8) + 3
   cmdInitValue = (tag << 18) | (bufferOffset << 5) | (bufferOffset + 1024);
   //*(&APC_CSU_CMDHIGHDATA0 + cmdFifoId) = 0;
   do_write((void *)(&APC_CSU_CMDFIFO0 + cmdFifoId), cmdInitValue | 0x40000010);
   //4. wait for dma complete
   if(isWait)
   {
      while(0 == (do_read_volatile((void *)(&APC_CSU_DMASTATUS)) & (1 << tag)));
   }
}

/*!
* @brief:  三维外部搬移到一维内部，外部地址不超过32位，寄存器组源用6目的用2
* @author:	chunmeng.li
* @Date:  2022年6月13日
* @param: addrSrc : 		 [源地址 ]
* @param: xNumSrc : 		 [源第一维度搬移字节数 ]
* @param: yNumSrc : 		 [源第二维度搬移个数 ]
* @param: yStepSrc :		 [源第二维度搬移步进字节数 ]
* @param: zStepSrc :		 [源第三维度搬移步进字节数 ]
* @param: addrDst : 		 [目的地址 ]
* @param: dataLen : 		 [搬移总字节数 ]
* @param: tag : 			 [DMA Tag: 0~31用于查询是否完成 ]
* @param: isWait :			 [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
void ape_csu_dma_3Dto1D_G2L_ch6ch2_simp_transfer(uint32_t addrSrc, uint16_t xNumSrc, uint16_t yNumSrc,
    uint32_t yStepSrc, uint32_t zStepSrc, uint32_t addrDst, uint32_t dataLen, uint8_t tag, uint8_t isWait)
{
    uint32_t cmdInitValue;
    //bufferOffset为链表端bufferID寄存器偏移word数（每个bufferId占8个word）
    uint32_t bufferOffset = (get_core_id() & 1) << 6;
    uint32_t cmdFifoId = (bufferOffset >> 5) | 1;

    //1.配置源端寄存器
    do_write((void *)(&APC_CSU_DMAADDRL6 + bufferOffset), addrSrc);
    do_write((void *)(&APC_CSU_DMAADDRH6 + bufferOffset), 0);
    do_write((void *)(&APC_CSU_DMAYSTEPL6 + bufferOffset), yStepSrc);
    do_write((void *)(&APC_CSU_DMAZSTEPL6 + bufferOffset), zStepSrc);
    do_write((void *)(&APC_CSU_DMAYNUMXNUM6 + bufferOffset), (yNumSrc << 16) | xNumSrc);
    do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM6 + bufferOffset), 0xF0000000 | dataLen);
    //2.配置目的端寄存器
    do_write((void *)(&APC_CSU_DMAADDRL2 + bufferOffset), addrDst);
    do_write((void *)(&APC_CSU_DMAADDRH2 + bufferOffset), 0);
    do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM2 + bufferOffset), 0x0E000000 | dataLen);
    //3.配置命令字
    bufferOffset <<= 5;
    //readBufferId为(bufferOffset>>8) + 4
    //writeBufferId为(bufferOffset>>8) + 3
    cmdInitValue = (tag << 18) | ((bufferOffset + 512) << 5) | (bufferOffset + 1536);
    //*(&APC_CSU_CMDHIGHDATA0 + cmdFifoId) = 0;
    do_write((void *)(&APC_CSU_CMDFIFO0 + cmdFifoId), cmdInitValue | 0x40000010);

    //4. wait for dma complete
    if(isWait)
    {
        while(0 == (do_read_volatile((void *)(&APC_CSU_DMASTATUS)) & (1 << tag)));
    }
}

/*!
* @brief: 一级多维单边链表参数配置简化版，用于SM（多维）->DM(一维)
DMA链表搬移链表和寄存器配置
*             暂时固定用寄存器组0 & 1
* @author:  chunmeng.li
* @Date:  2022年6月14日
* @param: *paramPtr :	  [链表参数空间指针:  起始地址64字节对齐]
* @param: paramCsuAddr : [链表参数空间地址，csu视角]
* @param: numNodes :	  [链表节点数 ]
* @param: dmaTag : 	      [DMA Tag: 0~31用于查询是否完成 ]
* @param: *xAddrSrcPtr :  [
链表各节点DMA搬移起始地址，csu视角，长度为链表节点数，该函数不支持＞32比特地址 ]
* @param: xNum :	      [链表每节点X维度搬移字节数，各节点xNum相同，最大65535Byte ]
* @param: *allNumPtr :    [链表每节点DMA搬移总字节数，长度为链表节点数 ]
* @param: addrDst :	      [目的端地址，为DM空间 ]
* @param: allNumTotal :   [DMA搬移总字节数 ]
* @param: waitFlag :      [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
void dma_1l3d1s_chain_G2L_ch0ch1_simp_config_start(uint32_t *paramPtr,
    uint32_t paramCsuAddr,
    uint32_t numNodes,
    uint32_t dmaTag,
    uint32_t *xAddrSrcPtr,
    uint16_t xNum,
    uint32_t *allNumPtr,
    uint32_t addrDst,
    uint32_t allNumTotal,
    uint32_t waitFlag)
{
    uint32_t nodeIdx, cmdFifoId, bufferOffset, cmdValue;
    uint32_t *currNodePtr = paramPtr;

    if (get_core_id() & 1)
    {
        bufferOffset = 64;
        cmdFifoId = 3;
        cmdValue = 0x400128D1;
    }
    else
    {
        bufferOffset = 0;
        cmdFifoId = 1;
        cmdValue = 0x400020D1;
    }
    cmdValue |= (dmaTag << 18);

    for (nodeIdx = 0; nodeIdx < numNodes; nodeIdx++)
    {
        //Cmddata Low32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_CMDDATAL_WORD_OFFSET] = cmdValue;
        //xAddr Low 32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_XADDRL_WORD_OFFSET] = xAddrSrcPtr[nodeIdx];
        //xNum
        currNodePtr[APC_DMA_CHAIN_L1_3D_CMDDATAH_XNUM_WORD_OFFSET] = xNum;
        //AllNum
        currNodePtr[APC_DMA_CHAIN_L1_3D_ALLNUM_WORD_OFFSET] = allNumPtr[nodeIdx];
        currNodePtr += APC_DMA_CHAIN_L1_3D_SIZE_WORD;
    }

    //1.配置链表端寄存器
    do_write((void *)(&APC_CSU_DMAADDRL0 + bufferOffset), paramCsuAddr);
    do_write((void *)(&APC_CSU_DMAADDRH0 + bufferOffset), 0);
    do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM0 + bufferOffset), numNodes);
    //2.配置寄存器端寄存器
    do_write((void *)(&APC_CSU_DMAADDRL1 + bufferOffset), addrDst);
    do_write((void *)(&APC_CSU_DMAADDRH1 + bufferOffset), 0);
    do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM1 + bufferOffset), 0x0E000000 | allNumTotal);

    //3.配置命令字
    do_write((void *)(&APC_CSU_CMDFIFO0 + cmdFifoId), cmdValue);

    //4. wait for dma complete
    if(waitFlag)
    {
        while(0 == (do_read_volatile((void *)(&APC_CSU_DMASTATUS)) & (1 << dmaTag)));
    }
}

/*!
* @brief:  一级一维双边链表参数配置，采用多维实现一维
*          
建议使用者尽量将遍历规模较小的参数提前在DM空间中配置好，同时简化该函数，只配置可
变参数
* @author:  chunmeng.li
* @Date:  2022年7月6日
* @param: *paramPtr :    [链表参数空间指针:  起始地址64字节对齐，长度为64Byte*节点数]
* @param: paramCsuAddr : [链表参数空间地址，csu视角]
* @param: *srcAddrPtr :  [链表各节点DMA读起始地址，长度为链表节点数，
                          该函数不支持＞32比特地址 ]
* @param: *dstAddrPtr :  [链表各节点DMA写起始地址，csu视角，长度为链表节点数，
                          该函数不支持＞32比特地址 ]
* @param: *allNumPtr :   [链表每节点DMA搬移总字节数，长度为链表节点数 ]
* @param: numNodes :     [链表节点数 ]
* @param: dmaTag :       [DMA Tag: 0~31用于查询是否完成 ]
* @param: waitFlag :      [是否等DMA结束函数返回0:不等待,1:等待 ]
*/
void dma_1l1d2s_chain_ch3_G2L_start(uint32_t *paramPtr,
    uint32_t paramCsuAddr,
    uint32_t *srcAddrPtr,
    uint32_t *dstAddrPtr,
    uint32_t *allNumPtr,
    uint32_t numNodes,
    uint32_t dmaTag,
	uint32_t waitFlag)
{
    uint32_t nodeIdx, cmdValue;
    uint32_t *currNodePtr = paramPtr;
    uint32_t apeOffset = get_core_id() & 1;
    uint32_t cmdFifoId = (apeOffset << 1) | 1;

    //1. cmddata配置为一级，多维，双边，bufferID=3+(apeid&1)*8，dmatag
    cmdValue = 0x00000315 | (apeOffset << 11) | (dmaTag << 18);
    //2. chain node config
    for (nodeIdx = 0; nodeIdx < numNodes; nodeIdx++)
    {
        //Cmddata Low32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_CMDDATAL_WORD_OFFSET] = cmdValue;
        //xAddr Low 32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_XADDRL_WORD_OFFSET] = srcAddrPtr[nodeIdx];
        //AllNum
        currNodePtr[APC_DMA_CHAIN_L1_3D_ALLNUM_WORD_OFFSET] = allNumPtr[nodeIdx];
        //Cmddata Low32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_SIZE_WORD + APC_DMA_CHAIN_L1_3D_CMDDATAL_WORD_OFFSET] = cmdValue;
        //xAddr Low 32bit
        currNodePtr[APC_DMA_CHAIN_L1_3D_SIZE_WORD + APC_DMA_CHAIN_L1_3D_XADDRL_WORD_OFFSET] = dstAddrPtr[nodeIdx];
        //AllNum
        currNodePtr[APC_DMA_CHAIN_L1_3D_SIZE_WORD + APC_DMA_CHAIN_L1_3D_ALLNUM_WORD_OFFSET] = allNumPtr[nodeIdx];
        currNodePtr += (APC_DMA_CHAIN_L1_3D_SIZE_WORD * 2);
    }
    apeOffset <<= 6;
    //3. 链表寄存器配置
    do_write((void *)(&APC_CSU_DMAADDRL3 + apeOffset), paramCsuAddr);
    do_write((void *)(&APC_CSU_DMAADDRH3 + apeOffset), 0);
    do_write((void *)(&APC_CSU_DMASIZEGRANALLNUM3 + apeOffset), numNodes);
    //4. 配置命令字
    do_write((void *)(&APC_CSU_CMDFIFO0 + cmdFifoId), cmdValue);
    //5. wait for dma complete
    if(waitFlag)
	{
		while(0 == (do_read_volatile((void *)(&APC_CSU_DMASTATUS)) & (1 << dmaTag)));
	}
}

 /*!   
 * @brief:  k值查找参数配置及CSU启动
 * @author:  chunmeng.li
 * @Date:  2022年6月11日
 * @param: bitSequenceAddr :  [比特序列地址，csu视角：512比特边界对齐，支持DM/SM ]
 * @param: seqIndexAddr :     [优先级索引数组地址，csu视角：512比特边界对齐，支持DM/SM ]
 * @param: seqLength :        [序列长度：N=32/64/128/256/512/1024 ]
 * @param: K0 :               [第一个K值入参：k+nPCwm ]
 * @param: K1 :               [第二个K值入参：k+nPC ]
 */
void qounit_findK_config_start(uint32_t bitSequenceAddr,
		uint32_t seqIndexAddr,
		uint32_t seqLength,
		uint32_t K0,
		uint32_t K1)
{
    //1. Q_F_Tmp Address config
	do_write((void *)(&APC_CSU_QOUCMDADDR), bitSequenceAddr);
    //2. Q_W index Sequence Address config
	do_write((void *)(&APC_CSU_QOUCMPDATA0), seqIndexAddr);
    //3. N: 32/64/128/256/512/1024
	do_write((void *)(&APC_CSU_QOUCMPDATA1), seqLength);
    //4. K0 & K1 config
	do_write((void *)(&APC_CSU_QOUCMPDATA2), (K1 << 16) | K0);
    //5. start cmd
	do_write((void *)(&APC_CSU_QOUCMDDATA), 0x80009009);
}

 /*!   
 * @brief:  16比特普通查找参数配置及CSU启动
 * @author:  chunmeng.li
 * @Date:  2022年6月11日
 * @param: compData :         [待查询数据，位宽16比特 ]
 * @param: seqAddr :          [序列地址，csu视角：512比特边界对齐 ]
 * @param: seqLength :        [序列长度：最大65535 ]
 */
void qounit_findEqual_config_start(uint32_t compData,
		uint32_t seqAddr,
		uint16_t seqLength)
{
    //1. compare data config
	do_write((void *)(&APC_CSU_QOUCMPDATA0), compData);
    //2. Sequence Address config
	do_write((void *)(&APC_CSU_QOUCMDADDR), seqAddr);
    //3. sequence length, QOUCMDPOS = 0
	do_write((void *)(&APC_CSU_QOUCMDNUM_QOUCMDPOS), seqLength << 16);
    //4. start cmd
	do_write((void *)(&APC_CSU_QOUCMDDATA), 0x80019308);
}

 /*!   
 * @brief:  等待QOunit处理完成
 * @author:  chunmeng.li
 * @Date:  2022年6月11日
 */
uint32_t qounit_wait_complete()
{
	uint32_t apeId = get_core_id() & 1;
	while (0 == (do_read_volatile((void *)(&APC_CSU_QOUSTATUS)) & (1 << (16 + apeId))));
	return do_read_volatile((void *)(&APC_CSU_QOUSTATUSPOPDATA));
}

/*!   
 * @brief:  等待QOunit处理完成
 * @author:  yufei.wang
 * @Date:  2022年6月11日
 */
//uint32_t test_cnt = 0;
int16_t qounit_equal_wait_complete()
{
    uint32_t data = 0xffff;
	uint32_t apeId = get_core_id() & 1;
	
	while (0 == (__ucps2_load_ext_mem_v((void *)(&APC_CSU_QOUSTATUS), f_W) & (1 << (16 + apeId))));

	data =  __ucps2_load_ext_mem_v((void *)(&APC_CSU_QOUSTATUSPOPDATA), f_W);
	if (0 == (data>>19))
	//if (0x4cf == ret)
	{
	    return (-1);
	}
	else
	{
	    return (data & 0xffff);
	}
}

uint32_t get_free_channel()
{
	while (1)
	{
		for (int tag = 0; tag < 32; tag++)
		{
			if (0 == (do_read_volatile(&APC_CSU_DMASTATUS) & (1<<tag)))
			{
				continue;
			}
			else
			{
				uint32_t temp = get_free_reg_group(tag);
				if (-1 == temp)
				{
					continue;
				}
				else
				{
					uint8_t regGroup = temp & 0xFF;
					uint8_t fifoNum = (temp>>16) & 0xFF;
					uint32_t ret = regGroup | (fifoNum<<8) | (tag<<16);
					return ret;
				}
			}
		}
	}
}

uint32_t spu_csu_dma_1D_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen)
{
	uint8_t regGroup = 0;
	uint8_t fifoNum = 0;
	uint8_t tag = 0;
	uint32_t temp = get_free_channel();
//	printf("get free channle: 0x%x. \r\n", temp);

	regGroup = temp & 0xFF;
	fifoNum = (temp>>8) & 0xFF;
	tag = (temp>>16)&0xFF;

	uint16_t offset_w = regGroup << 4;
	// src reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL0) + offset_w), addrSrc & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH0) + offset_w), (uint32_t)(addrSrc >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM0) + offset_w), ((0xF<<28) | (0xE<<24) | dataLen)); // DM,CGran=1,Gran=6;size=0xF
	// dst reg
	do_write(((uint32_t*)(&APC_CSU_DMAADDRL1) + offset_w), addrDst & 0xFFFFFFFF);
	do_write(((uint32_t*)(&APC_CSU_DMAADDRH1) + offset_w), (uint32_t)(addrDst >> 32));
	do_write(((uint32_t*)(&APC_CSU_DMASIZEGRANALLNUM1) + offset_w), ((0xF<<28) | (0xE<<24) | dataLen));
	
	stCsuDmaCmdL dmaCmdL;
	*(uint32_t*)(&dmaCmdL) = 0;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 0 + (regGroup<<1);
	dmaCmdL.idDst = 1 + (regGroup<<1);
	dmaCmdL.dmaTag = tag;
	do_write(((stCsuDmaCmdL*)(&APC_CSU_CMDFIFO0) + fifoNum), (*(uint32_t*)(&dmaCmdL)));

	return tag;
}

