DSP 应用

DSP 内存

DSP 内存架构

AmebaLite HiFi5 DSP 内存分为片内和片外两个区域。

  • DSP 片内内存 (DSP 独占)

    • ICache

    • DCache

    • DTCM

  • DSP 片外内存 (与 AmebaLite KM4,KR4 共享)

    • SRAM

    • PSRAM

这些内存在访问速度和容量上呈现显著差异。

在访问性能方面:

  • DTCM 和 DCache 具有最优的实时性,与 DSP 同频运行,可实现单周期数据获取;

  • SRAM 以 240MHz 频率和 64-bit 位宽提供次级性能;

  • PSRAM 虽标称 250MHz 频率,但因采用 16-bit 物理位宽(8-bit DDR),实际带宽最低。

在容量配置上呈现反向特性:

  • PSRAM 提供最大 16MB 可扩展空间(具体容量依芯片型号而定);

  • SRAM 总容量 512KB,扣除 KM4,KR4 处理器占用的 65KB 后,DSP 实际可用 447KB;

  • DTCM 和 DCache 作为专用高速存储,容量最小但延迟最低。

DSP 内存访问速度

DSP 内存传输性能对比

目标

内存访问速度 (MB/s)

搬运方式

SRAM

DTCM

1899

iDMA

SRAM

DCache

1791

memcpy

PSRAM

DTCM

430

iDMA

PSRAM

DCache

425

memcpy

备注

实验条件: DSP 500MHz, SRAM 240MHz, PSRAM 250MHz。

不同的实验条件下,内存访问速度可能不同。表格中的数据为测得的上限值。

DSP 内存访问方式

由于内存的访问速度会影响 DSP 的运算性能,甚至成为瓶颈。因此,运行在 DSP 上的算法应尽可能优先使用 DTCM, SRAM, 再使用 PSRAM。

实际应用时,可根据算法模型大小,选择不同的数据存放位置。例如:

  • 若模型小于 256KB,在程序启动后,可预先将数据全部加载到 DTCM 中并常驻。

  • 若模型很大,则可以在 PSRAM 和 DTCM 之间搬运数据。

有两种方式可以主动将数据从 PSRAM 搬运到 DTCM:

  • memcpy

  • iDMA

和 memcpy 相比, iDMA 的优势是可以释放 CPU 算力,在 iDMA 传输过程中 DSP 可以继续执行其他任务。然而, 在访问 PSRAM 的速度上,iDMA 并不具有明显优势

  • 搬运大数据块(64KB/128KB)时,iDMA 稍快。

  • 搬运小数据块(8KB/16KB/32KB)时, 反而 memcpy 更快。

iDMA 双缓冲区数据搬运样例

iDMA 的使用方式详见 xtensa 官方文档 sys_sw_rm.pdf, 第 8 章节 “The Integrated DMA Library API”。

本样例演示使用双缓冲区将数据从 PSRAM 搬运到 DTCM,边搬边算实现加速。

伪代码

 1#define ALIGN(x) __attribute__((aligned(x)))
 2#define DRAM0 __attribute__((section(".dram0.data")))
 3#define DRAM1 __attribute__((section(".dram1.data")))
 4
 5int8_t ALIGN(16) DRAM0 dst_ping[USER_BUFFER_SIZE];
 6int8_t ALIGN(16) DRAM1 dst_pong[USER_BUFFER_SIZE];
 7
 8#define NUM_DESCRIPTORS 2
 9IDMA_BUFFER_DEFINE(dmaBuffer, NUM_DESCRIPTORS, IDMA_1D_DESC);
10
11void idma_pingpong_buffers_example(void) {
12    idma_init(0, MAX_BLOCK_16, 16, TICK_CYCLES_1, 0, NULL);
13    idma_init_loop(dmaBuffer, IDMA_1D_DESC, NUM_DESCRIPTORS, NULL, NULL);
14
15    // prepare the first data
16    idma_copy_desc(dst_ping, ...);
17
18    // wait for the first idma finish
19    while (idma_buffer_status() > 0) {}
20
21                                            // prepare the second data
22                                            idma_copy_desc(dst_pong, src, size, 0);
23
24    // do the first process
25    user_process_1(dst_ping,....)
26
27                                            // wait for the second idma finish
28                                            while (idma_buffer_status() > 0) {}
29
30    // prepare the third data
31    idma_copy_desc(dst_ping, ...);
32
33                                            // do the second process
34                                            user_process_2(dst_pong,....)
35
36    // wait for the third idma finish
37    while (idma_buffer_status() > 0) {}
38                                            // prepare the fourth data
39                                            idma_copy_desc(dst_pong, src, size, 0);
40    // do the third process
41    user_process_3(dst_ping,....)
42                                            // wait for the fourth idma finish
43                                            while (idma_buffer_status() > 0) {}
44    // prepare the fifth data
45    idma_copy_desc(dst_ping, ...);
46                                            // do the fourth process
47                                            user_process_4(dst_pong,....)
48    ......
49}

代码包含以下几个部分:

  • 定义 iDMA 缓冲区

#define NUM_DESCRIPTORS 2
IDMA_BUFFER_DEFINE(dmaBuffer, NUM_DESCRIPTORS, IDMA_1D_DESC);
  • 初始化 iDMA

idma_init(0, MAX_BLOCK_16, 16, TICK_CYCLES_1, 0, NULL);
idma_init_loop(dmaBuffer, IDMA_1D_DESC, NUM_DESCRIPTORS, NULL, NULL);
  • 定义两块位于 DTCM 上的数据缓冲区

#define ALIGN(x) __attribute__((aligned(x)))
#define DRAM0 __attribute__((section(".dram0.data")))
#define DRAM1 __attribute__((section(".dram1.data")))

int8_t ALIGN(16) DRAM0 dst_ping[USER_BUFFER_SIZE];
int8_t ALIGN(16) DRAM1 dst_pong[USER_BUFFER_SIZE];
  • 第 N 次搬运,更新描述符并调度

idma_copy_desc(dst_X, src, size, 0);
while (idma_buffer_status() > 0) {}
user_process_N(dst_X,....)

在伪代码中,为了方便理解,将顺序执行的代码分为两列:

  • 左侧是 1,3,5,… 奇数次搬运和运算,使用 ping 数据缓冲区。

  • 右侧是 2,4,6,… 偶数次搬运和计算,使用 pong 数据缓冲区。

第 N 次的搬运和计算与第 N 以及第 N+1 次交杂在一起。搬运第 N 份数据的同时计算第 N-1 份数据,从而达到边搬运边计算的目的。

备注

用到 iDMA 时,需要在 DTCM 中放置少量的 iDMA 描述符数据,约几百字节,因此用户可使用的 DTCM 空间略小于 256KB。