#DSP开发流程
#整体框架
在地平线提供的Softmax算子示例中,展示了如何通过调度UCP框架实现DSP自定义算子的功能封装,您可以在OE包中的samples/ucp_tutorial/custom_operator/dsp_sample处获取示例源码进行同步阅读理解。
#Softmax算子开发
本章节主要以DSP Softmax算子开发为例为您介绍DSP算子开发的流程,对应地平线OE包中的samples/ucp_tutorial/custom_operator/dsp_sample/dsp_code/softmax/softmax_ivp.cc部分。
完整的算子开发分为如下三个步骤:
-
UCP API的调用,主要负责任务发起及计算资源分配;
-
DSP算子开发;
-
DSP算子注册运行。
#Softmax分析
Softmax算子可以拆分为以下四个基础计算:
-
计算输入元素中的最大值max。
-
计算并更新输入的每个元素: input = exp(input - max) 。
-
计算更新后input的和sum。
-
计算 output = input / sum 。
#UCP API
在此部分,将为您展示如何使用UCP接口申请内存资源、发起任务。以Softmax示例为例,由于UCP接口只支持输入输出这两个参数,如您需要将其它参数传递到DSP端,则需要进行一层封装,将封装后的“输入”传入接口,再由DSP从封装的“输入”中获取参数。
如下示例中,封装“输入”中包括三个字段,data_size为数据尺寸,示例中取100000,input字段为发送给dsp的数据地址,run_tcm_opt字段表示是否使用tcm优化实现。
// softmax op param
typedef struct {
int32_t data_size;
uint64_t input;
uint8_t run_tcm_opt;
} hbDSPSoftmaxParam;
// #define DEBUG_RESULT
int32_t test_softmax_op(int32_t argc, char **argv) {
LOGI("softmax op begin");
int32_t size = 100000;
int32_t data_size = size * sizeof(float32_t);
hbUCPSysMem input_mem, output_mem, output_opt_mem;
// prepare input data
hbUCPSysMem input_data;
HB_CHECK_SUCCESS(hbUCPMalloc(&input_data, data_size, 0),
"hbUCPMalloc input_data failed");
HB_CHECK_SUCCESS(hbDSPAddrMap(&input_data, &input_data),
"Failed to hbDSPAddrMap input_data");
float32_t *data_ptr = static_cast<float32_t *>(input_data.virAddr);
for (uint32_t i = 0U; i < size; ++i) {
data_ptr[i] = 1 + i / float32_t(size);
}
HB_CHECK_SUCCESS(hbUCPMalloc(&output_mem, data_size, 0),
"hbUCPMalloc output_mem failed");
HB_CHECK_SUCCESS(hbDSPAddrMap(&output_mem, &output_mem),
"Failed to hbDSPAddrMap output_mem");
HB_CHECK_SUCCESS(hbUCPMalloc(&output_opt_mem, data_size, 0),
"hbUCPMalloc output_opt_mem failed");
HB_CHECK_SUCCESS(hbDSPAddrMap(&output_opt_mem, &output_opt_mem),
"Failed to hbDSPAddrMap output_opt_mem");
HB_CHECK_SUCCESS(hbUCPMalloc(&input_mem, sizeof(hbDSPSoftmaxParam), 0),
"hbUCPMalloc input_mem failed");
HB_CHECK_SUCCESS(hbDSPAddrMap(&input_mem, &input_mem),
"Failed to hbDSPAddrMap input_mem");
hbDSPSoftmaxParam *ptr = static_cast<hbDSPSoftmaxParam *>(input_mem.virAddr);
ptr->data_size = data_size;
ptr->input = input_data.phyAddr;
ptr->run_tcm_opt = 0;
// ctrl param
hbUCPSchedParam sched_param;
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
sched_param.backend = HB_UCP_DSP_CORE_0;
sched_param.priority = 0;
// run without ping-pong
hbUCPTaskHandle_t task{nullptr};
HB_CHECK_SUCCESS(hbDSPRpcV2(&task, &input_mem, &output_mem, HB_DSP_RPC_CMD_NN_B),
"hbDSPRpcV2 failed");
HB_CHECK_SUCCESS(hbUCPSubmitTask(task, &sched_param), "hbUCPSubmitTask failed");
HB_CHECK_SUCCESS(hbUCPWaitTaskDone(task, 0), "hbUCPWaitTaskDone failed");
HB_CHECK_SUCCESS(hbUCPReleaseTask(task), "hbUCPReleaseTask failed");
// run without ping-pong
ptr->run_tcm_opt = 1;
task = nullptr;
HB_CHECK_SUCCESS(hbDSPRpcV2(&task, &input_mem, &output_opt_mem, HB_DSP_RPC_CMD_NN_B),
"hbDSPRpcV2 failed");
HB_CHECK_SUCCESS(hbUCPSubmitTask(task, &sched_param), "hbUCPSubmitTask failed");
HB_CHECK_SUCCESS(hbUCPWaitTaskDone(task, 0), "hbUCPWaitTaskDone failed");
HB_CHECK_SUCCESS(hbUCPReleaseTask(task), "hbUCPReleaseTask failed");
float32_t *dst_data_ptr = static_cast<float32_t *>(output_mem.virAddr);
float32_t *dst_opt_data_ptr =
static_cast<float32_t *>(output_opt_mem.virAddr);
bool flag{true};
for (uint32_t i = 0U; i < size; ++i) {
if (std::abs(dst_data_ptr[i] - dst_opt_data_ptr[i]) /
std::abs(dst_data_ptr[i]) >
1e-6) {
LOGE("opt vs non-opt result is diff {}, dst {} opt {}", i,
dst_data_ptr[i], dst_opt_data_ptr[i]);
flag = false;
break;
}
}
if (flag) {
LOGI("opt vs non-opt result is same");
}
hbDSPAddrUnmap(&input_mem);
hbUCPFree(&input_mem);
hbDSPAddrUnmap(&output_mem);
hbUCPFree(&output_mem);
hbDSPAddrUnmap(&output_opt_mem);
hbUCPFree(&output_opt_mem);
hbDSPAddrUnmap(&input_data);
hbUCPFree(&input_data);
LOGI("softmax op finish");
return 0;
}
#DSP算子实现
本章节将对如何实现上一节中提到的四个基础运算从而实现DSP Softmax算子进行介绍。
Cadence实现了一些基础数学运算,方便您进行开发。您可以从Cadence的基础示例中获取源码,也可从地平线直接获取编译好的依赖库dsp_math。
为了充分利用硬件性能,您需要了解DSP特性并使用好这些特性(VLIW、SIMD)。在进行开发时,可参照Cadence本身已实现的基础运算。
vecmaxf SIMD实现如下:
/**
* DSP find max value
* @param[in] x: input
* @param[in] N: length
* @return maximum value
*/
float32_t vecmaxf(const float32_t *x, int32_t N) {
const xb_vecN_2xf32 *restrict px;
valign al_px;
xb_vecN_2xf32 vecmax0, vecmax1, vecx0, vecx1;
vboolN_2 b_max0, b_max1;
int32_t n, N_tail, Nb_tail;
float32_t max;
// ASSERT( x );
if (N <= 0) return 0.f;
px = (const xb_vecN_2xf32 *)x;
al_px = IVP_LAN_2XF32_PP(px);
vecmax0 = vecmax1 = minus_inff_.f;
/* Main loop: process by 2*IVP_N_2 values per iteration */
for (n = 0; n<N>> (LOG2_IVP_N_2 + 1); n++) {
IVP_LAN_2XF32_IP(vecx0, al_px, px);
IVP_LAN_2XF32_IP(vecx1, al_px, px);
vecmax0 = IVP_MAXN_2XF32(vecx0, vecmax0);
vecmax1 = IVP_MAXN_2XF32(vecx1, vecmax1);
}
/* Process last N%(2*IVP_N_2) values */
N_tail = N & (2 * IVP_N_2 - 1);
Nb_tail = N_tail * sizeof(float32_t);
b_max0 = IVP_LTRSN_2(N_tail);
b_max1 = IVP_LTRSN_2(N_tail - IVP_N_2);
IVP_LAVN_2XF32_XP(vecx0, al_px, px, Nb_tail);
IVP_LAVN_2XF32_XP(vecx1, al_px, px, Nb_tail - IVP_N_2 * sizeof(float32_t));
IVP_MAXN_2XF32T(vecmax0, vecx0, vecmax0, b_max0);
IVP_MAXN_2XF32T(vecmax1, vecx1, vecmax1, b_max1);
/* Reduce maximium values from vectors to the scalar one */
#ifdef IVP_SELI_32B_ROTATE_RIGHT_16
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
vecmax1 = IVP_SELN_2XF32I(vecmax0, vecmax0, IVP_SELI_32B_ROTATE_RIGHT_16);
#endif
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
vecmax1 = IVP_SELN_2XF32I(vecmax0, vecmax0, IVP_SELI_32B_ROTATE_RIGHT_8);
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
vecmax1 = IVP_SELN_2XF32I(vecmax0, vecmax0, IVP_SELI_32B_ROTATE_RIGHT_4);
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
vecmax1 = IVP_SELN_2XF32I(vecmax0, vecmax0, IVP_SELI_32B_ROTATE_RIGHT_2);
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
vecmax1 = IVP_SELN_2XF32I(vecmax0, vecmax0, IVP_SELI_32B_ROTATE_RIGHT_1);
vecmax0 = IVP_MAXN_2XF32(vecmax0, vecmax1);
max = IVP_MOVF32_FROMN_2XF32(vecmax0);
return max;
} /* vecmaxf() */
vecexpf_max SIMD实现如下:
/**
* DSP Vectorized Floating-Point Exponential
* The exponential (or anti-logarithm) function computes the exponential
* value e to the power of input vector x[N], and stores the result to output
* vector z[N].
* @param[out] z: output
* @param[in] x: input
* @param[in] N: length
* @param[in] max_value: max_value
* @return maximum value
*/
void vecexpf_max(float32_t *z, const float32_t *x, int32_t N,
float32_t max_value) {
const xb_vecN_2xf32 *restrict px = (const xb_vecN_2xf32 *)x;
xb_vecN_2xf32 *restrict pz = (xb_vecN_2xf32 *)z;
xtfloat *restrict ptbl = (xtfloat *)expftblf;
// check correct
xb_vecN_2xf32 xmax = max_value;
xb_vecN_2xf32 xin, xin2, txin, zout;
xb_vecN_2xf32 p0, p1, p2, p3, p4, p5, p6;
xb_vecN_2xf32 scl1, scl2;
xb_vecN_2x32v t, exp_fract, exp_int, e1, e2;
xb_vecN_2x64w W;
xb_vecNx16 invln2;
valign xa, za;
vboolN_2 b_nan, b_max, b_inf;
#if EXPF_ERRH != 0
vboolN_2 b_edom, b_erange;
xb_int32v SCF; /* Floating-point Status and Control Register values. */
#endif
int32_t n;
/* common argument checks */
// NASSERT(x);
// NASSERT(z);
if (N <= 0) return;
/* load 1/ln(2) constant in Q30 */
invln2 =
IVP_MOVNX16_FROMN_2X32(IVP_LSN_2X32_I((const int32_t *)&invln2_Q30, 0));
za = IVP_ZALIGN();
xa = IVP_LAN_2XF32_PP(px);
for (n = 0; n<(N + IVP_N_2 - 1)>> LOG2_IVP_N_2; n++) {
IVP_LAVN_2XF32_XP(xin, xa, px, (uint8_t *)x + N * 4 - (uint8_t *)px);
txin = xin;
/* Check input for values that are out of domain/range */
b_nan = IVP_UNN_2XF32(xin, xin); /* x==NaN */
b_max = IVP_ULEN_2XF32(expfminmax[1].f, xin); /* x>=88.72284 or x==NaN */
b_inf = IVP_UEQN_2XF32(xin, plus_inff.f); /* x==+Inf or x==NaN */
/* Limit input values to [-128;+127] and replace NaNs *
* with some numbers to avoid unnecessary exceptions */
xin = IVP_SUBN_2XF32(xin, xmax);
xin = IVP_MOVN_2XF32T(127.0f, xin, b_max);
xin = IVP_MAXN_2XF32(xin, -128.0f);
/* Convert the input to Q24 and scale to 1/ln(2) */
t = IVP_TRUNCN_2XF32(xin, 24);
W = IVP_MULN_2X16X32_0(invln2, t);
IVP_MULAHN_2X16X32_1(W, invln2, t); /* Q24*Q30->Q54 */
/* Separate input to positive fractional part and integer part */
exp_fract = IVP_PACKVRNRN_2X64W(W, 22); /* Q54->Q32 */
exp_fract = IVP_SRLIN_2X32U(exp_fract, 1); /* Q32->Q31 */
exp_int = IVP_PACKHN_2X64W(W); /* Q54->Q22 */
exp_int = IVP_SRLIN_2X32(exp_int, 22); /* Q22->Q0 */
/* compute 2^fract in floating-point format */
xin =
IVP_FLOATN_2X32(exp_fract, 31); /* scale fraction part by 2^-31 with */
/* conversion from int32_t to float32_t */
/* Pass input to the output if x==+INF or x==NaN */
xin = IVP_MOVN_2XF32T(txin, xin, b_inf);
/* load polynomial coefficients */
IVP_LSRN_2XF32_IP(p0, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_IP(p1, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_IP(p2, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_IP(p3, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_IP(p4, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_IP(p5, ptbl, sizeof(float32_t));
IVP_LSRN_2XF32_XP(p6, ptbl, -6 * (int32_t)sizeof(float32_t));
/* compute polynomial using combination *
* of Estrin`s and Horner schemes */
xin2 = IVP_MULN_2XF32(xin, xin);
IVP_MULAN_2XF32(p1, xin, p0);
IVP_MULAN_2XF32(p3, xin, p2);
IVP_MULAN_2XF32(p5, xin, p4);
IVP_MULAN_2XF32(p3, xin2, p1);
IVP_MULAN_2XF32(p5, xin2, p3);
IVP_MULAN_2XF32(p6, xin, p5);
/* Apply integer exponential part to the result */
exp_int = IVP_ADDN_2X32(exp_int, 254);
e1 = IVP_SRLIN_2X32(exp_int, 1);
e2 = IVP_SUBN_2X32(exp_int, e1);
e1 = IVP_SLLIN_2X32(e1, 23);
e2 = IVP_SLLIN_2X32(e2, 23);
scl1 = IVP_MOVN_2XF32_FROMN_2X32(e1);
scl2 = IVP_MOVN_2XF32_FROMN_2X32(e2);
zout = IVP_MULN_2XF32(p6, scl1);
zout = IVP_MULN_2XF32(zout, scl2);
IVP_SAVN_2XF32_XP(zout, za, pz, (uint8_t *)z + N * 4 - (uint8_t *)pz);
}
IVP_SAPOSN_2XF32_FP(za, pz);
} /* vecexpf() */
vecsum SIMD实现如下:
/**
* DSP Vectorized sum value
* @param[in] x: input
* @param[in] N: length
* @return sum of x[N] value
*/
static float32_t vecsum(const float32_t *x, int32_t N) {
const xb_vecN_2xf32 *restrict px;
valign al_px;
xb_vecN_2xf32 vecsum0, vecsum1, vecx0, vecx1;
vboolN_2 b_sum0, b_sum1;
int32_t n, N_tail, Nb_tail;
float32_t sum;
// ASSERT(x);
if (N <= 0) return 0.f;
px = (const xb_vecN_2xf32 *)x;
al_px = IVP_LAN_2XF32_PP(px);
vecsum0 = vecsum1 = 0.f;
for (n = 0; n<N>> (LOG2_IVP_N_2 + 1); n++) {
IVP_LAN_2XF32_IP(vecx0, al_px, px);
IVP_LAN_2XF32_IP(vecx1, al_px, px);
vecsum0 = IVP_ADDN_2XF32(vecx0, vecsum0);
vecsum1 = IVP_ADDN_2XF32(vecx1, vecsum1);
}
N_tail = N & (2 * IVP_N_2 - 1);
Nb_tail = N_tail * sizeof(float32_t);
b_sum0 = IVP_LTRSN_2(N_tail);
b_sum1 = IVP_LTRSN_2(N_tail - IVP_N_2);
IVP_LAVN_2XF32_XP(vecx0, al_px, px, Nb_tail);
IVP_LAVN_2XF32_XP(vecx1, al_px, px, Nb_tail - IVP_N_2 * sizeof(float32_t));
IVP_ADDN_2XF32T(vecsum0, vecx0, vecsum0, b_sum0);
IVP_ADDN_2XF32T(vecsum1, vecx1, vecsum1, b_sum1);
vecsum0 = IVP_ADDN_2XF32(vecsum0, vecsum1);
sum = IVP_RADDN_2XF32(vecsum0);
return sum;
}除法运算可变为乘法运算,同时实现乘法运算比较容易且性能较好。
/**
* DSP Vectorized multiply
* @param[out] z: output
* @param[in] x1: multiply left input
* @param[in] x2: multiply right input
* @param[in] N: length
*/
static void vecmul(float32_t *z, const float32_t *x1, const float32_t x2,
int32_t N) {
const xb_vecN_2xf32 *restrict px;
xb_vecN_2xf32 *restrict pz;
valign al_px, al_pz;
px = (const xb_vecN_2xf32 *)x1;
pz = (xb_vecN_2xf32 *)z;
xb_vecN_2xf32 px2 = x2;
al_px = IVP_LAN_2XF32_PP(px);
al_pz = IVP_ZALIGN();
int32_t n, modN;
modN = (N & (IVP_N_2 - 1)) * sizeof(float32_t);
xb_vecN_2xf32 in, out;
for (n = 0; n<N>> LOG2_IVP_N_2; n++) {
IVP_LAN_2XF32_IP(in, al_px, px);
out = IVP_MULN_2XF32(in, px2);
IVP_SAN_2XF32_IP(out, al_pz, pz);
}
IVP_LAN_2XF32_IP(in, al_px, px);
out = IVP_MULN_2XF32(in, px2);
IVP_SAVN_2XF32_XP(out, al_pz, pz, modN);
IVP_SAPOSN_2XF32_FP(al_pz, pz);
}完整的Softmax实现如下:
static int32_t dsp_softmax(float32_t *input, int32_t length,
float32_t *output) {
float32_t max_value = vecmaxf(input, length);
DSP_LOGD("max_value %f", max_value);
vecexpf_max(output, input, length, max_value);
float32_t sum = vecsum(output, length);
DSP_LOGD("sum %f", sum);
int32_t i = 0;
float32_t div = 1 / sum;
vecmul(output, output, div, length);
return 0;
}以上Softmax实现直接访问DDR,由于J6没有配置向量缓存,故直接访问DDR内存非常耗时。常见的优化实现是,将数据分成tile块,通过idma搬移到TCM(TCM与缓存延迟相当)中,直接访问TCM进行计算。并且,为了将计算与idam搬运操作并行,DSP配备了两块TCM,以实现ping pong DMA的操作,如下图所示。
ping pong DMA的实现参考代码如下:
/**
* DSP tile Vectorized max
* @param[out] output_addr: tcm output addr
* @param[in] in_addr: tcm input addr
* @param[in] size: tcm compute size
* @param[out] max: max value addr: max value addr
* @param[in] param: input param
*/
void tile_max(float32_t *out_addr, float32_t *in_addr, int32_t size,
float32_t *max, float32_t param) {
float32_t max_value = vecmaxf(in_addr, size);
DSP_LOGD("max_value %f, size %d", max_value, size);
*max = max_value > *max ? max_value : *max;
}
/**
* DSP tile Vectorized sum
* @param[out] output_addr: tcm output addr
* @param[in] in_addr: tcm input addr
* @param[in] size: tcm compute size
* @param[out] sum: sum value addr: sum value addr
* @param[in] param: input param
*/
void tile_sum(float32_t *out_addr, float32_t *in_addr, int32_t size,
float32_t *sum, float32_t param) {
vecexpf_max(out_addr, in_addr, size, param);
*sum += vecsum(out_addr, size);
}
/**
* DSP tile Vectorized mul
* @param[out] output_addr: tcm output addr
* @param[in] in_addr: tcm input addr
* @param[in] size: tcm compute size
* @param[out] out: out value addr: out value addr
* @param[in] param: input param
*/
void tile_mul(float32_t *out_addr, float32_t *in_addr, int32_t size,
float32_t *out, float32_t param) {
float32_t div = 1.0f / param;
vecmul(out_addr, in_addr, div, size);
}
/**
* DSP softmax ping-pong framework
* @param[out] dst: optional
* @param[in] src
* @param[in] length
* @param[in] func: framework tile compute func
* @param[out] func_out: func output param
* @param[in] func_in: func input param
* @param[in] with_out_idma: whether need to copy back output
* @return 0 if success, return defined error code otherwise
*/
int32_t hb_dsp_ping_pong_frame(float32_t *dst, float32_t *src, void *src_idma[],
void *dst_idma[], int32_t length,
void (*func)(float32_t *, float32_t *, int32_t,
float32_t *, float32_t),
float32_t *func_out, float32_t func_in,
bool with_out_idma = true) {
int32_t size[] = {0, 0};
int32_t vret_in[2];
int32_t vret_out[2];
int32_t i = 0;
int32_t block_start = 0;
int32_t output_start = 0;
int32_t ping_pong = 0;
int32_t block = XT_MIN(TILE_SIZE >> 2, length);
int32_t block_size = block << 2;
size[0] = block;
float32_t sum = 0.f;
IDMA_COPY(HB_IDMA_CH0, vret_in[0], src, src_idma[0], block_size)
if (vret_in[0] < 0) {
DSP_LOGE("copy task failed!");
}
block_start += block;
if (block_start < length) {
block = XT_MIN(block, length - block_start);
block_size = block << 2;
size[1] = block;
IDMA_COPY(HB_IDMA_CH1, vret_in[1], src + block_start, src_idma[1],
block_size)
if (vret_in[1] < 0) {
DSP_LOGE("copy task failed!");
}
block_start += block;
}
while (i < length) {
i += size[ping_pong];
IDMA_WAIT(ping_pong, vret_in[ping_pong])
if (with_out_idma) {
IDMA_WAIT(ping_pong, vret_out[ping_pong])
}
func((float32_t *)dst_idma[ping_pong], (float32_t *)src_idma[ping_pong],
size[ping_pong], func_out, func_in);
if (with_out_idma) {
IDMA_COPY(ping_pong, vret_out[ping_pong], dst_idma[ping_pong],
dst + output_start, size[ping_pong] << 2)
if (vret_out[ping_pong] < 0) {
DSP_LOGE("Fail to copy softmax output, status %d", vret_out[ping_pong]);
return -1;
}
output_start += size[ping_pong];
}
if (block_start < length) {
block = XT_MIN(block, length - block_start);
block_size = block << 2;
size[ping_pong] = block;
IDMA_COPY(ping_pong, vret_in[ping_pong], src + block_start,
src_idma[ping_pong], block_size)
if (vret_in[ping_pong] < 0) {
DSP_LOGE("copy task failed!");
}
block_start += block;
}
ping_pong ^= 0x1;
}
if (with_out_idma) {
IDMA_WAIT(ping_pong ^ 0x01, vret_out[ping_pong ^ 0x01])
}
return 0;
}完整的DSP softmax实现如下:
#define PROF
/*
* hb_dsp_softmax have two implementation, ping-pong and direct call ddr
* 1. ping-pong: use idma to transfer ddr data to tcm, then use tile compute(simd optimization)
* 2. direct ddr: use simd(same with tile compute) to directly load and store ddr
* 3. profiling: ping-pong use 5154293 cycles, direct ddr use 7648254 cycles on j6 in this scenario
*/
int32_t hb_dsp_softmax(void *input, void *output, void *tm) {
DSP_LOGD("enter hb_dsp_softmax\n");
hbDSPSoftmaxParam *ptr = (hbDSPSoftmaxParam *)(input);
float32_t *src = (float32_t *)hb_dsp_mem_map(ptr->input, ptr->data_size);
PRT_IF_COND_RETURN(MAP_FAILED == src, HB_ERR_MMAP_FAILED)
float32_t *dst = (float32_t *)(output);
// ping-pong optimization
if (ptr->run_tcm_opt) {
xvTileManager *tm_ = (xvTileManager *)tm;
float32_t *src_0 = (float32_t *)(xvAllocateBuffer(
tm_, TILE_SIZE, XV_MEM_BANK_COLOR_0, IVP_2N));
float32_t *dst_0 = src_0;
float32_t *src_1 = (float32_t *)(xvAllocateBuffer(
tm_, TILE_SIZE, XV_MEM_BANK_COLOR_1, IVP_2N));
float32_t *dst_1 = src_1;
void *src_idma[] = {src_0, src_1};
void *dst_idma[] = {dst_0, dst_1};
int32_t length = ptr->data_size >> 2;
DSP_LOGD("length is %d\n", length);
#ifdef PROF
TIME_STAMP(cycles_start);
#endif
// find max
DSP_LOGD("find max\n");
float32_t max_value = 0.f;
hb_dsp_ping_pong_frame(dst, src, src_idma, dst_idma, length, tile_max,
&max_value, 0, false);
DSP_LOGD("max_value %f", max_value);
// exp & sum
DSP_LOGD("exp & sum");
float32_t sum_value = 0.f;
hb_dsp_ping_pong_frame(dst, src, src_idma, dst_idma, length, tile_sum,
&sum_value, max_value, true);
DSP_LOGD("sum_value %f", sum_value);
// mul
DSP_LOGD("mul");
hb_dsp_ping_pong_frame(dst, dst, src_idma, dst_idma, length, tile_mul, 0,
sum_value, true);
xvFreeBuffer(tm_, (void *)src_0);
xvFreeBuffer(tm_, (void *)src_1);
#ifdef PROF
TIME_STAMP(cycles_stop);
self_cycles = cycles_stop - cycles_start;
DSP_LOGI("find softmax ping-pong tcm is %d cycles", self_cycles);
#endif
} else {
// direct call ddr, because use simd, so not need flush cache
#ifdef PROF
TIME_STAMP(cycles_start);
#endif
int32_t N = ptr->data_size >> 2;
int32_t ret = dsp_softmax(src, N, dst);
if (ret != 0) {
DSP_LOGE("Run softmax op fail:%d", ret);
return ret;
}
#ifdef PROF
TIME_STAMP(cycles_stop);
self_cycles = cycles_stop - cycles_start;
DSP_LOGI("find softmax ddr tcm is %d cycles", self_cycles);
#endif // PROF
}
hb_dsp_mem_unmap((uint32_t)src);
return 0;
}
#注册运行
算子开发完成后,通过调用hb_dsp_register_fn接口注册hb_dsp_softmax算子,完成注册后,编译DSP镜像。
typedef int (*handle_fn)(void *input, void *output, void *tm);
/**
* register custom op, should be called before hb_dsp_start
* @param[in] latency: op latency(microseconds), if no latency info, set 0
* @return 0 if success, return defined error code otherwise
*/
int hb_dsp_register_fn(int cmd, handle_fn handle, int latency);如果是x86仿真运行,则无需启动镜像,如果是上板运行,需要执行脚本dsp_deploy.sh启动镜像。
