This repository has been archived on 2023-07-17. You can view files and clone it, but cannot push or open issues or pull requests.
bl_mcu_sdk/docs/source/notes/note_dma.rst
2022-12-07 10:19:27 +08:00

99 lines
4.1 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

DMA 链表模式LLI深度解析
===============================
博流系列芯片的 DMA 都支持链表模式LLI
在进行一次 DMA 读或者写的时候,可以配置多个链表,从而当一个链表的数据传输完成时,会跳到下一个链表的起始地址,并继续传输数据,直到链表的下一个地址为 0。如果 DMA 使能了完成中断,则当 DMA 发送或者接收完成时,会进入完成中断。
.. figure:: img/dma_lli.svg
:alt:
那么有了这种 DMA 链表模式,我们就可以实现以下功能:
- DMA 发送或者接收长度不限制
- DMA 发送接收地址可以不连续
- DMA 实现多种中断模式半中断、3中断、4中断等等
- DMA 实现循环功能
OK那么当我们开始研究链表配置之前我们需要了解一些前提
- 每个链表最多传输 4095 ,单位根据位宽决定
- 每个链表都可以触发中断
支持长度不限制
--------------------
由于每个 dma 链表最多支持 4905假设位宽用的是字节那么一个链表最多传输 4095 字节,很显然这个不能满足我们需求,性能太低。那么如何提高传输长度呢?
我们可以使用多个链表串接起来这样就能够支持更大的传输长度了并且传输的地址是连续的dma 链表连接如图所示:
.. figure:: img/dma_lli.png
:alt:
这个时候还有一个问题,当一个链表使用了 4095 字节,下一个链表是从 4095 的偏移开始,这个时候,就会产生非对齐的问题,如果是在 cache 场景下,是会有问题的。
因此,我们将 4095 减少到 4064这样保证每个链表的首地址都是 32 字节对齐的。到这就实现了长度不限制的功能,具体实现参考 ``bflb_dma_lli_config`` 函数。
.. code-block:: c
:linenos:
void bflb_dma_lli_config(struct bflb_device_s *dev, struct bflb_dma_channel_lli_pool_s *lli_pool, uint32_t lli_count, uint32_t src_addr, uint32_t dst_addr, uint32_t transfer_offset, uint32_t last_transfer_len)
{
uint32_t channel_base;
union bflb_dma_lli_control_s dma_ctrl_cfg;
channel_base = dev->reg_base;
dma_ctrl_cfg = (union bflb_dma_lli_control_s)getreg32(channel_base + DMA_CxCONTROL_OFFSET);
dma_ctrl_cfg.bits.TransferSize = 4064;
dma_ctrl_cfg.bits.I = 0;
/* nbytes will be integer multiple of 4064*n or 4064*2*n or 4064*4*n,(n>0) */
for (uint32_t i = 0; i < lli_count; i++) {
lli_pool[i].src_addr = src_addr;
lli_pool[i].dst_addr = dst_addr;
lli_pool[i].nextlli = 0;
if (dma_ctrl_cfg.bits.SI) {
src_addr += transfer_offset;
}
if (dma_ctrl_cfg.bits.DI) {
dst_addr += transfer_offset;
}
if (i == lli_count - 1) {
dma_ctrl_cfg.bits.TransferSize = last_transfer_len;
dma_ctrl_cfg.bits.I = 1;
}
if (i) {
lli_pool[i - 1].nextlli = (uint32_t)(uintptr_t)&lli_pool[i];
}
lli_pool[i].control = dma_ctrl_cfg;
}
}
支持地址不连续
--------------------
刚刚我们解决了长度限制问题,那么本身 dma 链表是支持地址不连续的,我们只需要把上面使用的多个链表当成一个大链表,然后两个大链表拼接,并且两个链表传输的首地址不连续,就可以实现地址不连续了。
具体实现参考 ``bflb_dma_channel_lli_reload`` 函数中的 ``bflb_dma_channel_lli_transfer_s``。dma 链表连接如图所示:
.. figure:: img/dma_lli2.png
:alt:
支持多中断
--------------------
完成了上述两步以后,多中断也就完成了。
当我们支持完长度不限制后,最后一个链表会开启中断,当传输完成最后一个链表时,就会触发中断。
当我们支持完地址不连续后,多个大链表(也就是长度不限制的链表的最后一个链表)完成都会触发中断,假设设定了三个传输 ``bflb_dma_channel_lli_transfer_s``, 那么会触发三次 DMA 完成中断。
支持循环模式
--------------------