DMA(直接内存访问)控制器是现代SoC设计中不可或缺的组件,它能够在不需要CPU介入的情况下,直接在内存与设备之间高效地搬运数据。Arm CoreLink DMA-350作为一款高性能DMA控制器,广泛应用于各种嵌入式系统和芯片设计中。
CoreLink DMA-350控制器具有以下几个显著特点:
64位地址支持:通过CH_LINKADDR和CH_LINKADDRHI寄存器组合,可以支持完整的64位地址空间访问,满足大内存系统的需求。在实际应用中,当ADDR_WIDTH配置大于32时,CH_LINKADDRHI寄存器才会生效。
命令链式执行:控制器支持通过LINKADDREN位(CH_LINKADDR寄存器bit0)实现命令的链式加载。当该位设置为1时,DMAC会从LINKADDR指定的地址获取下一条命令。这种机制特别适合需要连续执行多个DMA操作的场景。
双安全域操作:控制器提供了完整的非安全(DMANSECCTRL)和安全(DMASECCTRL)两个独立的控制域,每个域都有自己的状态和控制寄存器。这种设计使得DMA-350能够很好地适应现代安全敏感的应用场景。
灵活的触发机制:支持硬件触发输入和输出,可以通过寄存器配置选择特定的触发源和目标,为复杂的数据流处理提供了硬件级的同步能力。
DMA-350的寄存器组织采用了分层设计:
code复制DMAC顶层
├── DMANSECCTRL(非安全控制域)
│ ├── NSEC_CHINTRSTATUS0(通道中断状态)
│ ├── NSEC_STATUS(全局状态)
│ ├── NSEC_CTRL(全局控制)
│ └── ...
├── DMASECCTRL(安全控制域)
│ ├── SEC_CHINTRSTATUS0
│ ├── SEC_STATUS
│ ├── SEC_CTRL
│ └── ...
└── DMACH<n>(通道级寄存器)
├── CH_LINKADDR(链接地址低32位)
├── CH_LINKADDRHI(链接地址高32位)
├── CH_ERRINFO(错误信息)
└── ...
每个DMA通道都有自己独立的一组寄存器,用于配置和控制该通道的行为。同时,全局的控制和状态寄存器提供了对整个DMA控制器的管理接口。
命令链式执行是DMA-350的一个重要特性,它允许一个DMA操作完成后自动加载并执行下一个操作,而不需要CPU干预。这一功能主要通过以下寄存器实现:
CH_LINKADDR寄存器(偏移量0x078)
| 位域 | 名称 | 描述 |
|---|---|---|
| 31:2 | LINKADDR | 链接地址指针[31:2]。当LINKADDREN置1时,DMAC从此地址获取下一条命令 |
| 1 | - | 保留位,读为0写无效 |
| 0 | LINKADDREN | 链接地址使能。1:DMAC从LINKADDR获取下一条命令;0:当前命令完成后空闲 |
重要提示:链接命令必须清除LINKADDREN位来标记命令链的结束,否则会导致同一命令的无限循环执行。
CH_LINKADDRHI寄存器(偏移量0x07C)
| 位域 | 名称 | 描述 |
|---|---|---|
| 31:0 | LINKADDRHI | 链接地址指针[63:32]。当ADDR_WIDTH>32时有效,实现64位地址支持 |
在实际编程中,设置命令链的基本流程如下:
DMA-350提供了详细的错误诊断机制,主要通过CH_ERRINFO寄存器实现:
CH_ERRINFO寄存器(偏移量0x090)
| 位域 | 名称 | 描述 |
|---|---|---|
| 31:16 | ERRINFO | 错误详细信息,具体含义参考错误处理章节 |
| 7 | STREAMERR | 流接口错误标志。流接口发生错误时置1 |
| 4 | TRIGOUTSELERR | 触发输出选择错误。命令选择了该通道不允许的触发输出时置1 |
| 3 | DESTRIGINSELERR | 目标触发输入选择错误。命令选择了不允许的目标触发输入时置1 |
| 2 | SRCTRIGINSELERR | 源触发输入选择错误。命令选择了不允许的源触发输入时置1 |
| 1 | CFGERR | 配置错误。DMA命令启用或链接命令读取时,发现不支持的模式配置时置1 |
| 0 | BUSERR | 总线错误。DMA在数据或命令读取传输期间遇到总线错误时置1 |
在错误处理实践中,建议采用以下流程:
DMA-350提供了一个独特的工作寄存器查看机制,允许开发者实时监控DMA通道的内部状态:
CH_WRKREGPTR寄存器(偏移量0x088)
| 位域 | 名称 | 描述 |
|---|---|---|
| 3:0 | WRKREGPTR | 工作寄存器指针,选择要查看的内部寄存器 |
WRKREGPTR的可选值对应不同的内部寄存器:
CH_WRKREGVAL寄存器(偏移量0x08C)
| 位域 | 名称 | 描述 |
|---|---|---|
| 31:0 | WRKREGVAL | 工作寄存器值。显示由WRKREGPTR选择的内部寄存器当前值 |
注意:这些值只有在STAT_PAUSED状态置位时才保证稳定,否则可能不可预测。
非安全域提供了对非安全通道的统一管理:
NSEC_CTRL寄存器(偏移量0x00C)
关键控制位包括:
NSEC_CHCFG寄存器(偏移量0x018)
用于配置单个非安全通道的属性:
安全域的控制寄存器与非安全域类似,但增加了额外的安全控制:
SEC_CTRL寄存器(偏移量0x00C)
特有的安全控制位:
典型的DMA通道初始化流程:
c复制// 示例:初始化DMA通道
void dma_channel_init(uint32_t ch_num, uint64_t src, uint64_t dest, uint32_t size)
{
// 选择通道
write_reg(DMA_BASE + CH_SEL_OFFSET, ch_num);
// 设置源地址
write_reg(DMA_BASE + CH_SRCADDR_OFFSET, (uint32_t)(src & 0xFFFFFFFF));
write_reg(DMA_BASE + CH_SRCADDRHI_OFFSET, (uint32_t)(src >> 32));
// 设置目标地址
write_reg(DMA_BASE + CH_DESTADDR_OFFSET, (uint32_t)(dest & 0xFFFFFFFF));
write_reg(DMA_BASE + CH_DESTADDRHI_OFFSET, (uint32_t)(dest >> 32));
// 设置传输大小
write_reg(DMA_BASE + CH_XSIZE_OFFSET, size);
// 启用通道
write_reg(DMA_BASE + CH_CTRL_OFFSET, CH_ENABLE_MASK);
}
实现DMA命令链的示例代码:
c复制// 定义DMA命令结构
typedef struct {
uint32_t ctrl;
uint32_t src_addr_lo;
uint32_t src_addr_hi;
uint32_t dest_addr_lo;
uint32_t dest_addr_hi;
uint32_t xsize;
// 其他字段...
} dma_cmd_t;
// 设置命令链
void setup_command_chain(uint32_t ch_num, dma_cmd_t *cmds, uint32_t num_cmds)
{
// 确保命令在内存中是连续的
dma_cmd_t *cmd_array = allocate_contiguous_memory(num_cmds * sizeof(dma_cmd_t));
// 填充命令数组
for (uint32_t i = 0; i < num_cmds; i++) {
cmd_array[i] = cmds[i];
if (i < num_cmds - 1) {
// 不是最后一个命令,设置链接
cmd_array[i].ctrl |= LINK_ENABLE_MASK;
}
}
// 设置第一个命令地址
uint64_t first_cmd_addr = (uint64_t)cmd_array;
write_reg(DMA_BASE + CH_LINKADDR_OFFSET(ch_num), (uint32_t)(first_cmd_addr & 0xFFFFFFFF));
write_reg(DMA_BASE + CH_LINKADDRHI_OFFSET(ch_num), (uint32_t)(first_cmd_addr >> 32));
// 启用链接
uint32_t reg = read_reg(DMA_BASE + CH_LINKADDR_OFFSET(ch_num));
write_reg(DMA_BASE + CH_LINKADDR_OFFSET(ch_num), reg | LINKADDREN_MASK);
}
健壮的DMA错误处理应该包括:
c复制void dma_error_handler(uint32_t ch_num)
{
uint32_t err_info = read_reg(DMA_BASE + CH_ERRINFO_OFFSET(ch_num));
if (err_info & BUSERR_MASK) {
// 处理总线错误
handle_bus_error();
}
if (err_info & CFGERR_MASK) {
// 处理配置错误
handle_config_error();
}
// 清除错误标志
write_reg(DMA_BASE + CH_ERRCLR_OFFSET(ch_num), err_info);
// 可能需要重新初始化通道
dma_channel_reset(ch_num);
}
合理使用命令链:对于连续的多段传输,使用命令链可以减少CPU干预,提高效率。
缓冲区对齐:确保源和目标地址按照数据宽度对齐(如32位传输使用4字节对齐),可以显著提高传输效率。
突发传输:配置合适的ISSUSECAP值(CH_ISSUECAP寄存器),最大化总线利用率。
电源管理:利用IDLERETEN位(在SEC_CTRL/NSEC_CTRL中)在通道空闲时进入低功耗状态。
利用工作寄存器视图:通过CH_WRKREGPTR和CH_WRKREGVAL寄存器查看内部状态,特别适合调试复杂的传输问题。
触发调试:使用硬件触发机制同步DMA操作与调试工具,可以精确捕捉特定时刻的状态。
状态监控:定期检查CH_ERRINFO和通道状态寄存器,及早发现问题。
压力测试:在极限条件下(如最大传输速率、最小间隔等)测试DMA性能,确保稳定性。
问题1:DMA传输停止不前
排查步骤:
问题2:数据传输错误
排查步骤:
问题3:性能不达预期
优化检查: