一 程序位置分布RTOS中DSMC代码，drv_dsmc_host.c是主机驱动代码，drv_dsmc_slave.c是从机驱动代码，dsmc_test.c是测试程序 SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/tests/dsmc_test.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c 二 主机驱动代码解析drv_dsmc_host.c -> hal_dsmc_host.c -> drv_dsmc_host.c -> dsmc_test.c2.1 系统如何启动驱动？首先，在drv_dsmc_host.c主机驱动中rockchip_dsmc_host_probe(void)函数下方，使用了INIT_DEVICE_EXPORT(rockchip_dsmc_host_probe)将rockchip_dsmc_host_probe注册到了设备自动初始化函数这是RT-Thread自动初始化机制，听起来似乎很高大上，实际上只是将函数入口地址记录，在board_base.c中的rt_hw_board_init()启动板卡，完成基础初始化后，会在调用rt_components_board_init()，rt_components_board_init()内部实质上是执行函数指针，也就是执行对应的rockchip_dsmc_host_probe函数来启动驱动。2.2 主机驱动初始化代码流向rockchip_dsmc_host_probe -> HAL_DSMC_HOST_Init -> DSMC_HOST_DataInit -> DSMC_HOST_MapInit -> DSMC_HOST_LbInit -> DSMC_HOST_LbCmnConfig -> DSMC_HOST_LbCmnRgnConfig -> DSMC_HOST_LbCsrConfig -> lbReadReg与lbWriteReg在底层lbReadReg与lbWriteReg会去读写寄存器配置，然后逐级返回完成初始化打印HAL_DSMC_HOST_Init OK2.3 主机驱动代码框架drv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现在RTOS中drv_dsmc_host.c的rockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设从下图drv_dsmc_host.c函数列表中可以看出来，除了初始化外设，主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口在drv_dsmc_host.c中，除了初始化函数，还对dsmc_host_ops这个ops操作集合做读写与底层拷贝等函数的实现，本质上类似 Linux 的 file_operations 或 platform_driver 的 ops，统一抽象了对硬件的访问接口（read/write/copy_from/copy_to等），便于上层调用和适配不同实现，（参考Linux通用的驱动篇）给ops赋值函数指针之后，在上层应用函数，就可以直接使用一个dsmc_host_ops类型的结构体，调用其读写函数，拿到DSMC通信数据struct dsmc_host_ops { rt_err_t (*read)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, unsigned long addr, uint32_t *data); rt_err_t (*write)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, unsigned long addr, uint32_t val); rt_err_t (*copy_from)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, uint32_t from, uint32_t dst_phys, size_t size); rt_err_t (*copy_from_state)(struct rockchip_rt_dsmc_host *dsmc_host); rt_err_t (*copy_to)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, uint32_t src_phys, uint32_t to, size_t size); rt_err_t (*copy_to_state)(struct rockchip_rt_dsmc_host *dsmc_host); }; static struct dsmc_host_ops rockchip_dsmc_ops = { .read = DSMC_HOST_ReadData, .write = DSMC_HOST_WriteData, .copy_from = DSMC_HOST_CopyFrom, .copy_from_state = DSMC_HOST_CopyFromState, .copy_to = DSMC_HOST_CopyTo, .copy_to_state = DSMC_HOST_CopyToState, }; 三 从机驱动代码解析drv_dsmc_slave.c -> hal_dsmc_slave.c -> drv_dsmc_slave.c -> dsmc_test.c3.1 系统如何启动驱动？与主机代码基本一致，drv_dsmc_slave.c中rockchip_dsmc_slave_probe(void)函数下方，使用了INIT_DEVICE_EXPORT(rockchip_dsmc_slave_probe)将rockchip_dsmc_slave_probe注册到了设备自动初始化函数3.2 从机驱动初始化代码流向rockchip_dsmc_slave_probe -> HAL_DSMC_SLAVE_Init -> WRITE_REG -> rockchip_dsmc_slave_probe 在底层WriteReg会去读写寄存器配置，然后逐级返回完成初始化打印rockchip dsmc local bus slave driver initialized3.3 从机驱动代码框架从机代码比较简单，只需要初始化写寄存器，然后配置中断函数即可，主机发来的数据可以直接读取dmaHAL_DSMC_SLAVE_IrqHander是中断处理入口，判断是否要启动DMA，DSMC_SLAVE_DmaTrigger则负责具体执行通知硬件搬运数据的动作，它们一起实现了从机对主机搬运请求的响应和触发static HAL_Status DSMC_SLAVE_DmaTrigger(struct LBC_SLAVE_CSR_REG *pReg) { int timeOut = 1000; /* wait interrupt register empty */ while (timeOut-- > 0) { if (!(READ_REG(pReg->LBC_S2H_INT_STA) & (0x1 << S2H_INT_FOR_DMA_NUM))) { break; } if (timeOut == 0) { HAL_DBG_ERR("Timeout waiting for s2h interrupt empty!\n"); return HAL_TIMEOUT; } HAL_CPUDelayUs(1); } /* trigger a slave to host interrupt which will start dma hardware mode copy */ WRITE_REG(pReg->APP_CON15, 0x1); return HAL_OK; } /** * @brief DSMC_SLAVE irq hander. * @param slave: pointer to a DSMC_SLAVE structure. * @return HAL_Status */ HAL_Status HAL_DSMC_SLAVE_IrqHander(struct HAL_DSMC_SLAVE *slave) { struct LBC_SLAVE_CSR_REG *pReg; HAL_ASSERT(IS_LBC_SLAVE_CSR_INSTANCE(slave->pReg)); pReg = slave->pReg; if (READ_REG(pReg->LBC_CON15)) { DSMC_SLAVE_DmaTrigger(pReg); } /* clear all h2s interrupt */ WRITE_REG(pReg->APP_H2S_INT_STA, LBC_SLAVE_CSR_APP_H2S_INT_STA_APP_H2S_INT_STA_MASK); return HAL_OK; }
一 前言在前面分析了驱动大致走向和函数，驱动细节我们无法分析，因为拿不到底层寄存器相关的细节文档所以我们应用的重中之重，就是需要仔细分析DMA读写，把DMA读写学会，就可以拿到数据给上层应用使用DMA本身没有绑定DSMC，但是使用DMA驱动时，会给DMA配置DSMC的内存地址，这样DMA读写直接按块访问内存中映射的DSMC应用请直接看后面的 三 外部调用例程做库文件 ，例程修改为库文件和头文件作为接口库，提供对外接口调用二 RTOS DMA读写分析2.1 DMA驱动大致内容具体驱动分析请看前面的DSMC代码分析之RTOSdrv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现在RTOS中drv_dsmc_host.c的rockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设从下图drv_dsmc_host.c函数列表中可以看出来，除了初始化外设，主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口2.2 DMA用户使用流程实际上是实现主控 → DSMC控制器 → Local Bus → FPGA等来做读写测试用户程序主要是通过DMA或直接地址读写向DSMC控制器写入读取数据，数据通过Local Bus总线，读写外部设备下面所有DMA相关函数copy_to、copy_from以及dma_memcpy函数，内部都通过rt_device_control对dma设置状态w完成DMA读写rt_device_control设置的参数最终在SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c文件中的rt_dma_control函数中通过switch来实现DMA具体操作 rt_device_control(dma, RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL, m2m_xfer); //请求 DMA 通道 rt_device_control(dma, RT_DEVICE_CTRL_DMA_SINGLE_PREPARE, m2m_xfer); //DMA 传输准备信号 rt_device_control(dma, RT_DEVICE_CTRL_DMA_START, m2m_xfer); //单次 DMA 传输准备 rt_device_control(dma, RT_DEVICE_CTRL_DMA_STOP, m2m_xfer); //停止 DMA rt_device_control(dma, RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL, m2m_xfer); //释放 DMA 通道程序结构大致就是下方这样的伪代码，从结构上我们就能看出来，主要是初始化后，通过ops操作集来读写内存拿到数据，或者通过DMA完成读写main() └──> dsmc_test() ├──> psram_simple_test() // PSRAM基本功能测试（CPU读写+数据校验） │ ├──> ops->write() // 向PSRAM写入测试数据 │ ├──> ops->read() // 从PSRAM读取测试数据 │ └──> 数据校验 ├──> dma_test_psram() // PSRAM DMA搬运测试（DMA搬运+数据校验） │ ├──> dma_memcpy() // 用DMA搬运数据到/从PSRAM │ └──> 数据校验 ├──> plc_simple_test() // PLC类外设基本功能测试（CPU读写+数据校验） │ ├──> ops->write() // 向PLC外设写入测试数据 │ ├──> ops->read() // 从PLC外设读取测试数据 │ ├──> ops->copy_to // 从DMA拷贝数据到FPGA │ ├──> ops->copy_to_state //拷贝状态 │ ├──> ops->copy_from// 从FPGA拷贝数据到DMA │ ├──> ops->copy_from_state//拷贝状态 │ └──> 数据校验 ├──> dma_test_lb() // Local Bus外设DMA搬运测试（DMA搬运+数据校验） │ ├──> dma_memcpy() // 用DMA搬运数据到/从Local Bus外设 │ └──> 数据校验 ├──> fpga_simple_test() // FPGA外设基本功能测试（CPU读写+数据校验） │ ├──> ops->write() // 向FPGA写入测试数据 │ ├──> ops->read() // 从FPGA读取测试数据 │ └──> 数据校验 ├──> dsmc_slave_latency_cpu() // CPU方式外设访问延迟测试（多次操作统计平均延迟） │ └──> ops->read()/ops->write()（循环多次） // 多次读/写外设，统计耗时 ├──> dsmc_slave_speed_cpu() // CPU方式外设带宽测试（大块数据读写统计带宽） │ └──> ops->read()/ops->write()（大块数据） // 用CPU搬运大块数据，统计带宽 └──> dsmc_slave_speed_dma() // DMA方式外设带宽测试（大块数据搬运统计带宽） └──> dma_memcpy()（大块数据） // 用DMA搬运大块数据，统计带宽1 内存搬运/性能测试 dma_memcpy:用DMA控制器实现内存到内存的拷贝，并和CPU memcpy做对比，测试速度 2 PSRAM/外设简单功能测试 psram_simple_test:依次对PSRAM做多轮写入/读取/校验，测试不同数据模式 3 DMA 读写 PSRAM/外设测试 dma_test_psram:DDR-> SRAM，PSRAM->DDR，PSRAM->PSRAM三种方向的DMA搬运 4 PLC（Local Bus）功能测试 plc_simple_test:用DSMC的驱动内部ops接口测试主机和Local Bus外设的数据搬运。先从DDR DMA写到外设，外设DMA读回DDR 5 CPU 访问延迟测试 dsmc_slave_latency_cpu:反复用CPU读/写DSMC外设的固定地址，统计总用时，计算单次访问的平均延迟。 6 CPU 访问带宽测试 dsmc_slave_speed_cpu:用CPU连续写入/读取DSMC外设的大块数据，统计总用时，计算带宽（MB/s） 7 DMA 访问带宽测试 dsmc_slave_speed_dma：用DMA连续搬运大块数据到DSMC外设，再搬回，测试DMA带宽（MB/s） 三 外部调用例程做库文件3.1 外部文件调用库文件代码本质已经完善了读写，我们只需要把例程改为库文件，就可以给其他程序使用了外部调用基本框架都可以不变，只需要把源文件放到同级目录，rt_dsmc_lib.h头文件加上，就可以在用户新文件调用 #include "rt_dsmc_lib.h" void dsmc_test(int argc, char **argv) { rt_device_t dev = rt_device_find("dsmc_host"); if (dev == RT_NULL) { rt_kprintf("dsmc_host not found\n"); return; } struct rockchip_rt_dsmc_host *dsmc_host = (struct rockchip_rt_dsmc_host *)dev; int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); // 只测试第0个cs psram_simple_test(dsmc_host, 0); dsmc_slave_latency_cpu(dsmc_host, 0); dsmc_slave_speed_cpu(dsmc_host, 0); dma_test_psram(dsmc_host, 0); plc_simple_test(dsmc_host, 0); } #ifdef RT_USING_FINSH #include <finsh.h> MSH_CMD_EXPORT(dsmc_test, dsmc tester); #endif 3.2 例程修改库文件rt_dsmc_lib.c #include "rt_dsmc_lib.h" #define IO_BW_32 0 #define IO_BW_16 1 #define IO_BW_8 2 #define IO_TYPE_0 0 #define DMA_SIZE (0x10000) #define COUNTS 100 static struct rt_device *dma; static rt_sem_t mem_sem; static void m2m_complete(void *param) { rt_sem_release(mem_sem); } void dma_memcpy(int *dst_mem, int *src_mem, int size) { struct rt_dma_transfer *m2m_xfer; rt_err_t ret; int i, len; uint32_t tick_s, tick_e; /* ms */ m2m_xfer = (struct rt_dma_transfer *)rt_malloc(sizeof(*m2m_xfer)); mem_sem = rt_sem_create("memSem", 0, RT_IPC_FLAG_FIFO); RT_ASSERT(m2m_xfer != RT_NULL); RT_ASSERT(src_mem != RT_NULL); RT_ASSERT(dst_mem != RT_NULL); RT_ASSERT(mem_sem != RT_NULL); len = size / sizeof(int); for (i = 0; i < len; i++) src_mem[i] = len - i; rt_memset(dst_mem, 0x0, size); rt_memset(src_mem, 0x6, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src_mem, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst_mem, size); dma = rt_device_find("dmac0"); /* memcpy test */ rt_memset(m2m_xfer, 0x0, sizeof(*m2m_xfer)); m2m_xfer->direction = RT_DMA_MEM_TO_MEM; m2m_xfer->dst_addr = (rt_uint32_t)dst_mem; m2m_xfer->src_addr = (rt_uint32_t)src_mem; m2m_xfer->len = size; m2m_xfer->callback = m2m_complete; m2m_xfer->cparam = m2m_xfer; ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) { /* dma copy start */ ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_SINGLE_PREPARE, m2m_xfer); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_START, m2m_xfer); RT_ASSERT(ret == RT_EOK); /* wait for complete */ ret = rt_sem_take(mem_sem, RT_WAITING_FOREVER); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_STOP, m2m_xfer); RT_ASSERT(ret == RT_EOK); } tick_e = HAL_GetTick(); ret = rt_memcmp(src_mem, dst_mem, size); rt_kprintf("dma memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) rt_memcpy(dst_mem, src_mem, size); tick_e = HAL_GetTick(); ret = rt_memcmp(dst_mem, src_mem, size); rt_kprintf("cpu memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); rt_sem_delete(mem_sem); rt_free(m2m_xfer); } int dma_test_psram(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma test1: copy from ddr to psram\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("dma test2: copy from psram to ddr\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); rt_kprintf("dma test3: copy from psram to psram\n"); src = (int *)map->phys; dst = (int *)(map->phys + size); dma_memcpy(dst, src, size); rt_kprintf("DMA test done\n"); return 0; } void psram_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i, j; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t test_cap; uint32_t read, write; uint32_t test_data[] = {0x5aa5f00f, 0x0, 0xffffffff, 0x3cc3d22d}; rt_kprintf("start cs%d simple test\n", cs); for (j = 0; j < 4; j++) { test_cap = map->size; rt_kprintf("write, test_cap = 0x%x\n", test_cap); rt_kprintf(" write\n"); for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, test_data[j]); } rt_kprintf("read\n"); test_cap = map->size; rt_kprintf(" read\n"); for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); if (read != test_data[j]) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, test_data[j], test_data[j] ^ read); } } } rt_kprintf("phase 1: write address to data\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i); } for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 2: write 0x0f0f5aa5 + address\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i + 0x0f0f5aa5); } test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i + 0x0f0f5aa5; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 3: dma copy by software\n"); dma_test_psram(dsmc_host, cs); rt_kprintf("cs%d simple test done.\n", cs); } int plc_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; int *src = NULL, *dst = NULL; uint32_t size = DMA_SIZE; int ret = 0; uint32_t timeout = 1000000; rt_kprintf("phase 1: dma memcopy from host DDR to slave memory\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); /* init src address */ rt_memset(src, 0x66, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_to(dsmc_host, cs, 0, (uint32_t)src, 0x0, size)) { while (ops->copy_to_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy to complete timeout\n"); ret = -1; goto err; } } rt_memcmp(src, (int *)map->phys, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_kprintf("phase2 : dma memcopy from slave memory to host DDR\n"); dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); rt_memset(dst, 0x88, size); /* init plc src address */ for (i = 0; i < size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0x10100000 + i); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_from(dsmc_host, cs, 0x0, 0, (uint32_t)dst, size)) { while (ops->copy_from_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy from complete timeout\n"); ret = -1; goto err; } } rt_memcmp((int *)map->phys, dst, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_dma_free(src); rt_dma_free(dst); rt_kprintf("plc_simple_test test done\n"); return 0; err: rt_kprintf("plc_simple_test test error!\n"); return ret; } void dsmc_slave_latency_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; uint32_t read, counter; uint32_t kt[2], diff; rt_kprintf("cpu access dsmc latency test\n"); counter = 1000000; kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->read(dsmc_host, cs, 3, 0x100, &read); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, read latency %uns\n", counter, diff, diff * 1000000 / counter); rt_kprintf("read = 0x%x\n", read); kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->write(dsmc_host, cs, 3, 0x100, 0x6); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, write latency %uns\n", counter, diff, diff * 1000000 / counter); } void dsmc_slave_speed_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t kt[2], rd_diff, wr_diff; uint32_t read; rt_kprintf("cpu access dsmc speed\n"); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0xffffffff); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, map->size); kt[1] = HAL_GetTick(); wr_diff = kt[1] - kt[0]; rt_kprintf("cpu write %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, wr_diff, map->size / wr_diff / 1000); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); } kt[1] = HAL_GetTick(); rd_diff = kt[1] - kt[0]; rt_kprintf("cpu read %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, rd_diff, map->size / rd_diff / 1000); } int dsmc_slave_speed_dma(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma access dsmc speed\n"); rt_kprintf("DMA write DSMC\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("DMA read DSMC\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); return 0; } 3.3 例程修改头文件RT_DSMC_LIB.H #ifndef __RT_DSMC_LIB_H__ #define __RT_DSMC_LIB_H__ #include <stdint.h> #include <rthw.h> #include <rtthread.h> #include "hal_base.h" #include "dma.h" #include "drv_dsmc_host.h" #ifdef __cplusplus extern "C" { #endif struct rockchip_rt_dsmc_host; // 供外部应用调用的测试接口 int dma_test_psram(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); void psram_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); void dsmc_slave_latency_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); void dsmc_slave_speed_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); int dsmc_slave_speed_dma(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); int plc_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs); void dma_memcpy(int *dst_mem, int *src_mem, int size); #ifdef __cplusplus } #endif #endif /* __RT_DSMC_LIB_H__ */ 四 Linux DMA例程 #include <stdio.h> #include <stdint.h> #include <time.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <linux/input.h> #include <errno.h> #include <string.h> #include <getopt.h> #include <stdbool.h> #include <libgen.h> #include <sys/select.h> #include <signal.h> const char * const VERSION = "1.0"; #define DMA_MEMCPY_PATH "/dev/dsmc/cs0/region0" /* commands codes */ #define DMA_MEMCPY_SETUP 0x01000000 #define DMA_MEMCPY_START 0x02000000 #define DMA_MEMCPY_GET_TIME 0x03000000 #define DMA_MEMCPY_SHUTDOWN 0x04000000 #define DSMC_MEM 0xc0000000 uint8_t *w_buffer = NULL; uint8_t *r_buffer = NULL; struct DMASetupParams { unsigned int dev_phy_addr; unsigned int dst_phy_addr; unsigned int transfer_direction; unsigned int transfer_single_size; unsigned int transfer_total_size; }; struct DMATransferInfo { float dma_r_rate; float dma_w_rate; uint32_t dma_r_cost_time; uint32_t dma_w_cost_time; uint32_t error_count; }; struct DmaParams { uint32_t address; uint32_t size; uint32_t cycles; char dev[64]; }; struct DMAInfo { int dma_fd; int input_fd; uint16_t *map_base_addr; uint16_t *vir_addr; struct DmaParams params; struct DMATransferInfo dma_trans_info; }; struct CmdLineParams { uint32_t address; uint32_t size; uint32_t cycles; char dev[64]; }; /* Short option names */ static const char g_shortopts [] = ":s:c:vh"; /* Option names */ static const struct option g_longopts [] = { { "size", required_argument, NULL, 's' }, { "cycles", required_argument, NULL, 'c' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; static void usage(char **argv) { fprintf(stdout, "Usage: %s [options]\n\n" "Options:\n" " -s | --size Data size (Unit: Byte) \n" " -c | --cycles Loop read and write times \n" " -v | --version Display version information\n" " -h | --help Show help content\n\n" "Example:\n" " # ./%s -a 0x10000000 -s 65536 -c 1000 \n" "", basename(argv[0]), basename(argv[0])); } static bool parse_parameter(struct CmdLineParams *params, int argc, char **argv) { int opt; memset(params, 0, sizeof(struct CmdLineParams)); while ((opt = getopt_long(argc, argv, g_shortopts, g_longopts, NULL)) != -1) { switch (opt) { case 's': params->size = atoi(optarg); break; case 'c': params->cycles = atoi(optarg); break; case 'h': usage(argv); exit(0); case 'v': printf("version : %s\n", VERSION); exit(0); default: fprintf(stderr, "Unknown option %c\n", optopt); break; } } return true; } //static void neon_copy(volatile void *dst, volatile void *src, int sz) static void neon_copy(void *dst, void *src, int sz) { if (sz & 63) { sz = (sz & -64) + 64; } memcpy(dst, src, sz); } static int dma_init(struct DMAInfo *dma_info) { if (dma_info == NULL) { return -1; } dma_info->dma_fd = open(DMA_MEMCPY_PATH, O_RDWR); if(dma_info->dma_fd < 0) { printf("can't open %s\n", DMA_MEMCPY_PATH); return -1; } return 0; } static void dma_destroy(struct DMAInfo *dma_info) { if (dma_info->map_base_addr != NULL) { munmap(dma_info->map_base_addr, dma_info->params.size); dma_info->map_base_addr = NULL; } if (dma_info->dma_fd != -1) close(dma_info->dma_fd); } static int dma_write(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int i = 0, ret = -1; struct input_event input_buf; int status = 0; struct timeval start, end, time_diff; fd_set input; struct timeval timeout; unsigned time2; float rate; /* Populate the write buffer */ for (i = 0; i < dma_info->params.size ; i++) { w_buffer[i] = i+1; } /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 0; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr write: 0x%x\n", dev_addr); return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } neon_copy((unsigned char *)dma_info->vir_addr, (unsigned char *)w_buffer, dma_info->params.size); /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while(!time2){ time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); dma_info->dma_trans_info.dma_w_cost_time = time2; dma_info->dma_trans_info.dma_w_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_w_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } static int dma_read(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int status = 0; int ret = -1, i = 0; struct input_event input_buf; struct timeval start, end, time_diff; fd_set input; unsigned time2; float rate; /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 1; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr read: 0x%x\n", dev_addr); return -1; } /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while(!time2){ time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); neon_copy((unsigned char *)r_buffer, (unsigned char *)dma_info->vir_addr, dma_info->params.size); /* Verify data */ for(i = 0; i < dma_info->params.size ; i ++) { if(r_buffer[i] != w_buffer[i]) { dma_info->dma_trans_info.error_count++; if(dma_info->dma_trans_info.error_count == 1) { printf("\nData verify error at offset %d (0x%x), expect: 0x%x, actual: 0x%x\n", i, i * 2, w_buffer[i], r_buffer[i]); } } } dma_info->dma_trans_info.dma_r_cost_time = time2; dma_info->dma_trans_info.dma_r_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_r_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } static void dma_test(struct DMAInfo *dma_info) { int ret = -1; unsigned int num = 0; float total_dma_w_rate = 0, total_dma_r_rate = 0; float max_dma_w_rate = 0, max_dma_r_rate = 0; float min_dma_w_rate = -1, min_dma_r_rate = -1; uint32_t total_dma_w_time = 0, total_dma_r_time = 0; uint32_t max_dma_w_time = 0, max_dma_r_time = 0; uint32_t min_dma_w_time = -1, min_dma_r_time = -1; const uint32_t total_cycles = dma_info->params.cycles; printf("\nStarting DMA test with %d cycles...\n\n", total_cycles); // Initialize total error count uint32_t total_error_count = 0; while (dma_info->params.cycles-- > 0) { ret = dma_write(dma_info); if (ret < 0) { printf("dma_write return error code: %d\r\n\n\n", ret); break; } #if 1 printf("write end ---------\n"); continue; #endif printf("start read----\n"); ret = dma_read(dma_info); if (ret < 0) { printf("dma_read return error code: %d\r\n\n\n", ret); break; } num++; // Calculate error rate for this cycle float error_rate = (dma_info->dma_trans_info.error_count / (float)(dma_info->params.size / 2)) * 100; // Accumulate total error count total_error_count += dma_info->dma_trans_info.error_count; dma_info->dma_trans_info.error_count = 0; // Print current cycle statistics printf("\rCycle %d/%d - Data Size: %d bytes | Write: %.2f MB/s | Read: %.2f MB/s | Write Time: %d us | Read Time: %d us | Error Rate: %.2f%%", num, total_cycles, dma_info->params.size, dma_info->dma_trans_info.dma_w_rate, dma_info->dma_trans_info.dma_r_rate, dma_info->dma_trans_info.dma_w_cost_time, dma_info->dma_trans_info.dma_r_cost_time, error_rate); fflush(stdout); // Accumulate for write average calculation total_dma_w_rate += dma_info->dma_trans_info.dma_w_rate; // Calculate the write maximum value if (dma_info->dma_trans_info.dma_w_rate > max_dma_w_rate) { max_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; } // Calculate the write minimum value if (min_dma_w_rate == -1.0 || dma_info->dma_trans_info.dma_w_rate < min_dma_w_rate) { min_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; } // Accumulate for read average calculation total_dma_r_rate += dma_info->dma_trans_info.dma_r_rate; // Calculate the write maximum value if (dma_info->dma_trans_info.dma_r_rate > max_dma_r_rate) { max_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; } // Calculate the write minimum value if (min_dma_r_rate == -1.0 || dma_info->dma_trans_info.dma_r_rate < min_dma_r_rate) { min_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; } // Accumulated is used to calculate the average time to write total_dma_w_time += dma_info->dma_trans_info.dma_w_cost_time; // Write maximum time calculation if (dma_info->dma_trans_info.dma_w_cost_time > max_dma_w_time) { max_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; } // Write minimum time calculation if (min_dma_w_time == -1.0 || dma_info->dma_trans_info.dma_w_cost_time < min_dma_w_time) { min_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; } // Accumulated is used to calculate the average time to read total_dma_r_time += dma_info->dma_trans_info.dma_r_cost_time; // Read maximum time calculation if (dma_info->dma_trans_info.dma_r_cost_time > max_dma_r_time) { max_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; } // Read minimum time calculation if (min_dma_r_time == -1.0 || dma_info->dma_trans_info.dma_r_cost_time < min_dma_r_time) { min_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; } } if (num > 0) { // Calculate average error rate float average_error_rate = (total_error_count / (float)(num * (dma_info->params.size / 2))) * 100; // Print final statistics printf("\n\nTest Complete! Final Statistics:\n"); printf("Completed Cycles: %d/%d\n", num, total_cycles); printf("Data Size: %d bytes\n", dma_info->params.size); printf("Write Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_w_rate / num); printf(" Time: %d us (avg)\n", total_dma_w_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_w_rate); printf(" Time: %d us (min)\n", min_dma_w_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_w_rate); printf(" Time: %d us (max)\n", max_dma_w_time); printf("\nRead Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_r_rate / num); printf(" Time: %d us (avg)\n", total_dma_r_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_r_rate); printf(" Time: %d us (min)\n", min_dma_r_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_r_rate); printf(" Time: %d us (max)\n", max_dma_r_time); printf("\nAverage Error Rate: %.2f%%\n", average_error_rate); } return; } int main(int argc, char *argv[]) { struct CmdLineParams params; struct DMAInfo dma_info; int ret = -1; if (parse_parameter(&params, argc, argv) == false) { printf("Please try --help to see usage.\n"); exit(1); } w_buffer = (uint8_t *)malloc(params.size); if (w_buffer == NULL) { printf("malloc w_buffer failed!\n"); exit(-1); } r_buffer = (uint8_t *)malloc(params.size); if (r_buffer == NULL) { free(w_buffer); printf("malloc r_buffer failed!\n"); exit(-1); } memset(&dma_info, 0, sizeof(struct DMAInfo)); dma_info.params.address = DSMC_MEM; dma_info.params.size = params.size; dma_info.params.cycles = params.cycles; ret = dma_init(&dma_info); if (ret < 0) { dma_destroy(&dma_info); return -1; } dma_test(&dma_info); dma_destroy(&dma_info); if (w_buffer != NULL) { free(w_buffer); } if (r_buffer != NULL) { free(r_buffer); } return 0; } 五 RTOS DMA例程/** * Copyright (c) 2024 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: Apache-2.0 ****************************************************************************** * @file dsmc_test.c * @version V1.0 * @brief dsmc test * * Change Logs: * Date Author Notes * 2024-10-10 Zhihuan He the first version * ****************************************************************************** */ #include <stdint.h> #include <rthw.h> #include <rtthread.h> #ifdef RT_USING_COMMON_TEST_DSMC #include "hal_base.h" #include "dma.h" #include "drv_dsmc_host.h" #define IO_BW_32 0 #define IO_BW_16 1 #define IO_BW_8 2 #define IO_TYPE_0 0 #define DMA_SIZE (0x10000) #define COUNTS 100 static struct rt_device *dma; static rt_sem_t mem_sem; static void m2m_complete(void *param) { rt_sem_release(mem_sem); } void dma_memcpy(int *dst_mem, int *src_mem, int size) { struct rt_dma_transfer *m2m_xfer; rt_err_t ret; int i, len; uint32_t tick_s, tick_e; /* ms */ m2m_xfer = (struct rt_dma_transfer *)rt_malloc(sizeof(*m2m_xfer)); mem_sem = rt_sem_create("memSem", 0, RT_IPC_FLAG_FIFO); RT_ASSERT(m2m_xfer != RT_NULL); RT_ASSERT(src_mem != RT_NULL); RT_ASSERT(dst_mem != RT_NULL); RT_ASSERT(mem_sem != RT_NULL); len = size / sizeof(int); for (i = 0; i < len; i++) src_mem[i] = len - i; rt_memset(dst_mem, 0x0, size); rt_memset(src_mem, 0x6, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src_mem, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst_mem, size); dma = rt_device_find("dmac0"); /* memcpy test */ rt_memset(m2m_xfer, 0x0, sizeof(*m2m_xfer)); m2m_xfer->direction = RT_DMA_MEM_TO_MEM; m2m_xfer->dst_addr = (rt_uint32_t)dst_mem; m2m_xfer->src_addr = (rt_uint32_t)src_mem; m2m_xfer->len = size; m2m_xfer->callback = m2m_complete; m2m_xfer->cparam = m2m_xfer; ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) { /* dma copy start */ ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_SINGLE_PREPARE, m2m_xfer); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_START, m2m_xfer); RT_ASSERT(ret == RT_EOK); /* wait for complete */ ret = rt_sem_take(mem_sem, RT_WAITING_FOREVER); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_STOP, m2m_xfer); RT_ASSERT(ret == RT_EOK); } tick_e = HAL_GetTick(); ret = rt_memcmp(src_mem, dst_mem, size); rt_kprintf("dma memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) rt_memcpy(dst_mem, src_mem, size); tick_e = HAL_GetTick(); ret = rt_memcmp(dst_mem, src_mem, size); rt_kprintf("cpu memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); rt_sem_delete(mem_sem); rt_free(m2m_xfer); } static int dma_test_psram(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma test1: copy from ddr to psram\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("dma test2: copy from psram to ddr\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); rt_kprintf("dma test3: copy from psram to psram\n"); src = (int *)map->phys; dst = (int *)(map->phys + size); dma_memcpy(dst, src, size); rt_kprintf("DMA test done\n"); return 0; } void psram_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i, j; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t test_cap; uint32_t read, write; uint32_t test_data[] = {0x5aa5f00f, 0x0, 0xffffffff, 0x3cc3d22d}; rt_kprintf("start cs%d simple test\n", cs); for (j = 0; j < 4; j++) { test_cap = map->size; rt_kprintf("write, test_cap = 0x%x\n", test_cap); rt_kprintf(" write\n"); for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, test_data[j]); } rt_kprintf("read\n"); test_cap = map->size; rt_kprintf(" read\n"); for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); if (read != test_data[j]) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, test_data[j], test_data[j] ^ read); } } } rt_kprintf("phase 1: write address to data\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i); } for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 2: write 0x0f0f5aa5 + address\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i + 0x0f0f5aa5); } test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i + 0x0f0f5aa5; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 3: dma copy by software\n"); dma_test_psram(dsmc_host, cs); rt_kprintf("cs%d simple test done.\n", cs); } static int plc_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; int *src = NULL, *dst = NULL; uint32_t size = DMA_SIZE; int ret = 0; uint32_t timeout = 1000000; rt_kprintf("phase 1: dma memcopy from host DDR to slave memory\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); /* init src address */ rt_memset(src, 0x66, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_to(dsmc_host, cs, 0, (uint32_t)src, 0x0, size)) { while (ops->copy_to_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy to complete timeout\n"); ret = -1; goto err; } } rt_memcmp(src, (int *)map->phys, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_kprintf("phase2 : dma memcopy from slave memory to host DDR\n"); dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); rt_memset(dst, 0x88, size); /* init plc src address */ for (i = 0; i < size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0x10100000 + i); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_from(dsmc_host, cs, 0x0, 0, (uint32_t)dst, size)) { while (ops->copy_from_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy from complete timeout\n"); ret = -1; goto err; } } rt_memcmp((int *)map->phys, dst, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_dma_free(src); rt_dma_free(dst); rt_kprintf("plc_simple_test test done\n"); return 0; err: rt_kprintf("plc_simple_test test error!\n"); return ret; } void dsmc_slave_latency_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; uint32_t read, counter; uint32_t kt[2], diff; rt_kprintf("cpu access dsmc latency test\n"); counter = 1000000; kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->read(dsmc_host, cs, 3, 0x100, &read); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, read latency %uns\n", counter, diff, diff * 1000000 / counter); rt_kprintf("read = 0x%x\n", read); kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->write(dsmc_host, cs, 3, 0x100, 0x6); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, write latency %uns\n", counter, diff, diff * 1000000 / counter); } void dsmc_slave_speed_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t kt[2], rd_diff, wr_diff; uint32_t read; rt_kprintf("cpu access dsmc speed\n"); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0xffffffff); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, map->size); kt[1] = HAL_GetTick(); wr_diff = kt[1] - kt[0]; rt_kprintf("cpu write %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, wr_diff, map->size / wr_diff / 1000); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); } kt[1] = HAL_GetTick(); rd_diff = kt[1] - kt[0]; rt_kprintf("cpu read %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, rd_diff, map->size / rd_diff / 1000); } static int dsmc_slave_speed_dma(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma access dsmc speed\n"); rt_kprintf("DMA write DSMC\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("DMA read DSMC\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); return 0; } void dsmc_test(int argc, char **argv) { uint32_t i; rt_device_t dev; struct rockchip_rt_dsmc_host *dsmc_host; rt_kprintf("this is dsmc_test\n"); dev = rt_device_find("dsmc_host"); if (dev == RT_NULL) { rt_kprintf("dsmc_host not found\n"); return; } dsmc_host = (struct rockchip_rt_dsmc_host *)dev; for (i = 0; i < DSMC_MAX_SLAVE_NUM; i++) { if (dsmc_host->host->dsmcHostDev.ChipSelCfg[i].deviceType == DEV_UNKNOWN) continue; psram_simple_test(dsmc_host, i); if (dsmc_host->host->dsmcHostDev.ChipSelCfg[i].deviceType == DEV_DSMC_LB) { plc_simple_test(dsmc_host, i); dsmc_slave_latency_cpu(dsmc_host, i); } dsmc_slave_speed_cpu(dsmc_host, i); dsmc_slave_speed_dma(dsmc_host, i); } } #ifdef RT_USING_FINSH #include <finsh.h> MSH_CMD_EXPORT(dsmc_test, dsmc tester); #endif #endif
一 前言在前面分析了驱动大致走向和函数，驱动细节我们无法分析，因为拿不到底层寄存器相关的细节文档所以我们应用的重中之重，就是需要仔细分析DMA读写，把DMA读写学会，就可以拿到数据给上层应用使用DMA本身没有绑定DSMC，但是使用DMA驱动时，会给DMA配置DSMC的内存地址，这样DMA读写直接按块访问内存中映射的DSMC应用请直接看后面的 三 外部调用例程做库文件 ，例程修改为库文件和头文件作为接口库，提供对外接口调用二 Linux DMA读写分析2.1 DMA驱动大致内容（创龙为dma_memcpy.c，飞凌为dsmc_lb_device.c）首先要写底层DMA读写驱动，提供用户空间与FPGA（localbus）之间的高速数据搬运通道，在dsmc-lb-device.c中增加DMA驱动读写接口（飞凌做法），或者单开一个DMA读写驱动（创龙做法），源码见SDKDMA有个dma_setup_params_stu结构体配置参数transfer_direction决定的读写，由DMA执行内存写到FPGA，还是FPGA读到内存其实在上层，我们只关心DMA读写就可以了，底层驱动已经配置好DSMC的DMA，我们读数据在DMA配置好后由DSMC读取完成再通知DMA读取，写数据由DMA发送后DSMC会完成写数据创建设备文件接口，通常为/dev/dsmc，然后通过ioctl接口进行DMA参数配置、启动、状态获取与关闭。（对应函数：dma_memcpy_ioctl）通过mmap为用户空间提供DMA缓冲区（对应函数：dma_memcpy_mmap）可选通过input事件（GPIO中断）通知用户空间DMA完成（对应函数：dma_irq_handler，上报input事件，创龙做法）ioctl命令（核心控制接口）通过ops调用dma_memcpy_ioctl在dma_memcpy_ioctl做switch分支，用以做DMA不同时机的操作 DMA_MEMCPY_SETUP 配置DMA参数 case DMA_MEMCPY_SETUP: DMA_MEMCPY_START 启动DMA传输 按配置开始一次DMA搬运 case DMA_MEMCPY_START: DMA_MEMCPY_GET_TIME 获取最近一次DMA耗时 返回微秒级耗时 case DMA_MEMCPY_GET_TIME: DMA_MEMCPY_SHUTDOWN 关闭DMA、释放资源 每次传输后调用 case DMA_MEMCPY_SHUTDOWN:上层应用读写必须依次完成setup/start/shutdown流程，完成DMA读写到DSMC实现的流程详细驱动分析参考前面的DSMC代码分析之Linux2.2 DMA用户使用流程用户每次DMA操作都需完整的setup/start/shutdown流程用户操作前置：1 分配读/写缓冲区：malloc等 2 打开设备节点和可选input事件设备（其实是dma_init）：open("/dev/dma_memcpy", O_RDWR)，open("/dev/input/eventX", O_RDONLY) 3 赋值DMAInfo结构：填充读取的地址，大小等写操作（内存→FPGA） dma_write()填充写src_buf缓冲区（写数组之类） 配置DMA参数（ioctl SETUP，方向为写） 执行ioctl(fd, DMA_MEMCPY_SETUP, &setup) mmap映射DMA缓冲区 对应函数：mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) memcpy数据到DMA缓冲区 对应函数：memcpy(dma_buf, src_buf, size) 启动DMA（ioctl START） 执行ioctl(fd, DMA_MEMCPY_START, 0) 等待input事件（创龙做法，飞凌直接下一步） 对应函数：select/read(input_fd, &event, sizeof(event)) 获取DMA耗时（ioctl GET_TIME） 执行ioctl(fd, DMA_MEMCPY_GET_TIME, 0) 关闭DMA（ioctl SHUTDOWN），释放mmap 执行ioctl(fd, DMA_MEMCPY_SHUTDOWN, 0)、munmap(dma_buf, size) 读操作（FPGA→内存） dma_read()配置DMA参数（ioctl SETUP，方向为读） 执行ioctl(fd, DMA_MEMCPY_SETUP, &setup) 启动DMA（ioctl START） 执行ioctl(fd, DMA_MEMCPY_START, 0) mmap映射DMA缓冲区 对应函数：mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) 等待input事件（select/read） 对应函数：select/read(input_fd, &event, sizeof(event)) 从DMA缓冲区拷贝到读缓冲区 对应函数：memcpy(dst_buf, dma_buf, size) 数据校验 对应函数：memcmp/自定义校验函数 获取DMA耗时（ioctl GET_TIME） 执行ioctl(fd, DMA_MEMCPY_GET_TIME, 0) 关闭DMA（ioctl SHUTDOWN），释放mmap 执行ioctl(fd, DMA_MEMCPY_SHUTDOWN, 0)、munmap(dma_buf, size) 伪代码流程main() ├─ parse_args(argc, argv) ├─ malloc w_buffer, r_buffer ├─ open("/dev/dma_memcpy") ├─ open("/dev/input/eventX") ├─ for (循环次数) │ ├─ 填充 w_buffer │ ├─ ioctl(fd, DMA_MEMCPY_SETUP, &setup) │ ├─ dma_buf = mmap(..., fd, ...) │ ├─ memcpy(dma_buf, w_buffer, size) │ ├─ ioctl(fd, DMA_MEMCPY_START, 0) │ ├─ select/read(input_fd, ...) │ ├─ ioctl(fd, DMA_MEMCPY_GET_TIME, 0) │ ├─ ioctl(fd, DMA_MEMCPY_SHUTDOWN, 0) │ ├─ munmap(dma_buf, size) │ ├─ 读操作同理 │ └─ 数据校验、统计 ├─ close(fd), close(input_fd), free()总之就是，open打开DMA节点，然后ioctl命令通过ops调用dma_memcpy_ioctl执行setup/start/shutdown流程，完成DMA配置、读写、关闭的流程三 外部调用例程做库文件3.1 外部文件调用库文件代码本质已经完善了读写，我们只需要把例程改为库文件，就可以给其他程序使用了首先把dma_write函数内部的下面这段注释，测试例程是通过这种方式填写测试数据 for (i = 0; i < dma_info->params.size; i++) { w_buffer[i] = i + 1; }外部调用基本框架都可以不变，只需要把源文件放到同级目录，dsmc_lib.h头文件加上，dsmc_test函数注释，改为dma_read和dma_write就可以。#include "dsmc_lib.h" int main(int argc, char *argv[]) { struct CmdLineParams params; struct DMAInfo dma_info; int ret = -1; if (parse_parameter(&params, argc, argv) == false) { printf("Please try --help to see usage.\n"); exit(1); } w_buffer = (uint8_t *)malloc(params.size); if (w_buffer == NULL) { printf("malloc w_buffer failed!\n"); exit(-1); } r_buffer = (uint8_t *)malloc(params.size); if (r_buffer == NULL) { free(w_buffer); printf("malloc r_buffer failed!\n"); exit(-1); } memset(&dma_info, 0, sizeof(struct DMAInfo)); dma_info.params.address = DSMC_MEM; dma_info.params.size = params.size; dma_info.params.cycles = params.cycles; ret = dma_init(&dma_info); if (ret < 0) { dma_destroy(&dma_info); return -1; } // dma_test(&dma_info); for (i = 0; i < dma_info->params.size; i++) { w_buffer[i] = i + 1; } dma_write(&dma_info); dma_read(&dma_info); printf("写入数据："); for (int i = 0; i < dma_info.params.size; i++) { printf("%02x ", w_buffer[i]); } printf("\n"); printf("读回数据："); for (int i = 0; i < dma_info.params.size; i++) { printf("%02x ", r_buffer[i]); } printf("\n"); dma_destroy(&dma_info); if (w_buffer != NULL) { free(w_buffer); } if (r_buffer != NULL) { free(r_buffer); } return 0; }3.2 例程改造后的库文件DSMC_LIB.C #include "dsmc_lib.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <errno.h> uint8_t *w_buffer = NULL; uint8_t *r_buffer = NULL; static void neon_copy(void *dst, void *src, int sz) { if (sz & 63) { sz = (sz & -64) + 64; } memcpy(dst, src, sz); } int dma_init(struct DMAInfo *dma_info) { if (dma_info == NULL) { return -1; } dma_info->dma_fd = open(DMA_MEMCPY_PATH, O_RDWR); if (dma_info->dma_fd < 0) { printf("can't open %s\n", DMA_MEMCPY_PATH); return -1; } return 0; } void dma_destroy(struct DMAInfo *dma_info) { if (dma_info->map_base_addr != NULL) { munmap(dma_info->map_base_addr, dma_info->params.size); dma_info->map_base_addr = NULL; } if (dma_info->dma_fd != -1) close(dma_info->dma_fd); } static int dma_write(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int i = 0, ret = -1; struct input_event input_buf; int status = 0; struct timeval start, end, time_diff; fd_set input; struct timeval timeout; unsigned time2; float rate; /* Populate the write buffer */ // for (i = 0; i < dma_info->params.size; i++) // { // w_buffer[i] = i + 1; // } /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 0; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr write: 0x%x\n", dev_addr); return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } neon_copy((unsigned char *)dma_info->vir_addr, (unsigned char *)w_buffer, dma_info->params.size); /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while (!time2) { time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); dma_info->dma_trans_info.dma_w_cost_time = time2; dma_info->dma_trans_info.dma_w_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_w_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } static int dma_read(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int status = 0; int ret = -1, i = 0; struct input_event input_buf; struct timeval start, end, time_diff; fd_set input; unsigned time2; float rate; /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 1; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr read: 0x%x\n", dev_addr); return -1; } /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while (!time2) { time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); neon_copy((unsigned char *)r_buffer, (unsigned char *)dma_info->vir_addr, dma_info->params.size); /* Verify data */ for (i = 0; i < dma_info->params.size; i++) { if (r_buffer[i] != w_buffer[i]) { dma_info->dma_trans_info.error_count++; if (dma_info->dma_trans_info.error_count == 1) { printf("\nData verify error at offset %d (0x%x), expect: 0x%x, actual: 0x%x\n", i, i * 2, w_buffer[i], r_buffer[i]); } } } dma_info->dma_trans_info.dma_r_cost_time = time2; dma_info->dma_trans_info.dma_r_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_r_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } void dma_test(struct DMAInfo *dma_info) { int ret = -1; unsigned int num = 0; float total_dma_w_rate = 0, total_dma_r_rate = 0; float max_dma_w_rate = 0, max_dma_r_rate = 0; float min_dma_w_rate = -1, min_dma_r_rate = -1; uint32_t total_dma_w_time = 0, total_dma_r_time = 0; uint32_t max_dma_w_time = 0, max_dma_r_time = 0; uint32_t min_dma_w_time = -1, min_dma_r_time = -1; const uint32_t total_cycles = dma_info->params.cycles; printf("\nStarting DMA test with %d cycles...\n\n", total_cycles); uint32_t total_error_count = 0; while (dma_info->params.cycles-- > 0) { // 填充写入buffer for (int i = 0; i < dma_info->params.size; i++) { w_buffer[i] = i + 1; } ret = dma_write(dma_info, w_buffer); if (ret < 0) { printf("dma_write return error code: %d\r\n\n\n", ret); break; } ret = dma_read(dma_info, r_buffer, w_buffer); if (ret < 0) { printf("dma_read return error code: %d\r\n\n\n", ret); break; } num++; float error_rate = (dma_info->dma_trans_info.error_count / (float)(dma_info->params.size)) * 100; total_error_count += dma_info->dma_trans_info.error_count; dma_info->dma_trans_info.error_count = 0; printf("\rCycle %d/%d - Data Size: %d bytes | Write: %.2f MB/s | Read: %.2f MB/s | Write Time: %d us | Read Time: %d us | Error Rate: %.2f%%", num, total_cycles, dma_info->params.size, dma_info->dma_trans_info.dma_w_rate, dma_info->dma_trans_info.dma_r_rate, dma_info->dma_trans_info.dma_w_cost_time, dma_info->dma_trans_info.dma_r_cost_time, error_rate); fflush(stdout); total_dma_w_rate += dma_info->dma_trans_info.dma_w_rate; if (dma_info->dma_trans_info.dma_w_rate > max_dma_w_rate) max_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; if (min_dma_w_rate == -1.0 || dma_info->dma_trans_info.dma_w_rate < min_dma_w_rate) min_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; total_dma_r_rate += dma_info->dma_trans_info.dma_r_rate; if (dma_info->dma_trans_info.dma_r_rate > max_dma_r_rate) max_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; if (min_dma_r_rate == -1.0 || dma_info->dma_trans_info.dma_r_rate < min_dma_r_rate) min_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; total_dma_w_time += dma_info->dma_trans_info.dma_w_cost_time; if (dma_info->dma_trans_info.dma_w_cost_time > max_dma_w_time) max_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; if (min_dma_w_time == -1.0 || dma_info->dma_trans_info.dma_w_cost_time < min_dma_w_time) min_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; total_dma_r_time += dma_info->dma_trans_info.dma_r_cost_time; if (dma_info->dma_trans_info.dma_r_cost_time > max_dma_r_time) max_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; if (min_dma_r_time == -1.0 || dma_info->dma_trans_info.dma_r_cost_time < min_dma_r_time) min_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; } if (num > 0) { float average_error_rate = (total_error_count / (float)(num * (dma_info->params.size / 2))) * 100; // Print final statistics printf("\n\nTest Complete! Final Statistics:\n"); printf("Completed Cycles: %d/%d\n", num, total_cycles); printf("Data Size: %d bytes\n", dma_info->params.size); printf("Write Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_w_rate / num); printf(" Time: %d us (avg)\n", total_dma_w_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_w_rate); printf(" Time: %d us (min)\n", min_dma_w_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_w_rate); printf(" Time: %d us (max)\n", max_dma_w_time); printf("\nRead Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_r_rate / num); printf(" Time: %d us (avg)\n", total_dma_r_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_r_rate); printf(" Time: %d us (min)\n", min_dma_r_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_r_rate); printf(" Time: %d us (max)\n", max_dma_r_time); printf("\nAverage Error Rate: %.2f%%\n", average_error_rate); } return; } 3.3 例程改造后的头文件DSMC_LIB.H #ifndef DSMC_LIB_H #define DSMC_LIB_H #include <stdint.h> #include <stdbool.h> #ifdef __cplusplus extern "C" { #endif extern uint8_t *w_buffer; extern uint8_t *r_buffer; #define DMA_MEMCPY_PATH "/dev/dsmc/cs0/region0" #define DMA_MEMCPY_SETUP 0x01000000 #define DMA_MEMCPY_START 0x02000000 #define DMA_MEMCPY_GET_TIME 0x03000000 #define DMA_MEMCPY_SHUTDOWN 0x04000000 #define DSMC_MEM 0xc0000000 struct DMASetupParams { unsigned int dev_phy_addr; unsigned int dst_phy_addr; unsigned int transfer_direction; unsigned int transfer_single_size; unsigned int transfer_total_size; }; struct DMATransferInfo { float dma_r_rate; float dma_w_rate; uint32_t dma_r_cost_time; uint32_t dma_w_cost_time; uint32_t error_count; }; struct DmaParams { uint32_t address; uint32_t size; uint32_t cycles; char dev[64]; }; struct DMAInfo { int dma_fd; int input_fd; uint16_t *map_base_addr; uint16_t *vir_addr; struct DmaParams params; struct DMATransferInfo dma_trans_info; }; // API接口 int dma_init(struct DMAInfo *dma_info); void dma_destroy(struct DMAInfo *dma_info); int dma_write(struct DMAInfo *dma_info); int dma_read(struct DMAInfo *dma_info); void dma_test(struct DMAInfo *dma_info); #ifdef __cplusplus } #endif #endif // DSMC_LIB_H 四 Linux DMA例程 #include <stdio.h> #include <stdint.h> #include <time.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <linux/input.h> #include <errno.h> #include <string.h> #include <getopt.h> #include <stdbool.h> #include <libgen.h> #include <sys/select.h> #include <signal.h> const char * const VERSION = "1.0"; #define DMA_MEMCPY_PATH "/dev/dsmc/cs0/region0" /* commands codes */ #define DMA_MEMCPY_SETUP 0x01000000 #define DMA_MEMCPY_START 0x02000000 #define DMA_MEMCPY_GET_TIME 0x03000000 #define DMA_MEMCPY_SHUTDOWN 0x04000000 #define DSMC_MEM 0xc0000000 uint8_t *w_buffer = NULL; uint8_t *r_buffer = NULL; struct DMASetupParams { unsigned int dev_phy_addr; unsigned int dst_phy_addr; unsigned int transfer_direction; unsigned int transfer_single_size; unsigned int transfer_total_size; }; struct DMATransferInfo { float dma_r_rate; float dma_w_rate; uint32_t dma_r_cost_time; uint32_t dma_w_cost_time; uint32_t error_count; }; struct DmaParams { uint32_t address; uint32_t size; uint32_t cycles; char dev[64]; }; struct DMAInfo { int dma_fd; int input_fd; uint16_t *map_base_addr; uint16_t *vir_addr; struct DmaParams params; struct DMATransferInfo dma_trans_info; }; struct CmdLineParams { uint32_t address; uint32_t size; uint32_t cycles; char dev[64]; }; /* Short option names */ static const char g_shortopts [] = ":s:c:vh"; /* Option names */ static const struct option g_longopts [] = { { "size", required_argument, NULL, 's' }, { "cycles", required_argument, NULL, 'c' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; static void usage(char **argv) { fprintf(stdout, "Usage: %s [options]\n\n" "Options:\n" " -s | --size Data size (Unit: Byte) \n" " -c | --cycles Loop read and write times \n" " -v | --version Display version information\n" " -h | --help Show help content\n\n" "Example:\n" " # ./%s -a 0x10000000 -s 65536 -c 1000 \n" "", basename(argv[0]), basename(argv[0])); } static bool parse_parameter(struct CmdLineParams *params, int argc, char **argv) { int opt; memset(params, 0, sizeof(struct CmdLineParams)); while ((opt = getopt_long(argc, argv, g_shortopts, g_longopts, NULL)) != -1) { switch (opt) { case 's': params->size = atoi(optarg); break; case 'c': params->cycles = atoi(optarg); break; case 'h': usage(argv); exit(0); case 'v': printf("version : %s\n", VERSION); exit(0); default: fprintf(stderr, "Unknown option %c\n", optopt); break; } } return true; } //static void neon_copy(volatile void *dst, volatile void *src, int sz) static void neon_copy(void *dst, void *src, int sz) { if (sz & 63) { sz = (sz & -64) + 64; } memcpy(dst, src, sz); } static int dma_init(struct DMAInfo *dma_info) { if (dma_info == NULL) { return -1; } dma_info->dma_fd = open(DMA_MEMCPY_PATH, O_RDWR); if(dma_info->dma_fd < 0) { printf("can't open %s\n", DMA_MEMCPY_PATH); return -1; } return 0; } static void dma_destroy(struct DMAInfo *dma_info) { if (dma_info->map_base_addr != NULL) { munmap(dma_info->map_base_addr, dma_info->params.size); dma_info->map_base_addr = NULL; } if (dma_info->dma_fd != -1) close(dma_info->dma_fd); } static int dma_write(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int i = 0, ret = -1; struct input_event input_buf; int status = 0; struct timeval start, end, time_diff; fd_set input; struct timeval timeout; unsigned time2; float rate; /* Populate the write buffer */ for (i = 0; i < dma_info->params.size ; i++) { w_buffer[i] = i+1; } /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 0; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr write: 0x%x\n", dev_addr); return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } neon_copy((unsigned char *)dma_info->vir_addr, (unsigned char *)w_buffer, dma_info->params.size); /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while(!time2){ time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); dma_info->dma_trans_info.dma_w_cost_time = time2; dma_info->dma_trans_info.dma_w_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_w_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } static int dma_read(struct DMAInfo *dma_info) { struct DMASetupParams dma_setup_params; unsigned int dev_addr = 0; int status = 0; int ret = -1, i = 0; struct input_event input_buf; struct timeval start, end, time_diff; fd_set input; unsigned time2; float rate; /* Setup DMA */ dma_setup_params.dev_phy_addr = dma_info->params.address; dma_setup_params.transfer_total_size = dma_info->params.size; dma_setup_params.transfer_single_size = dma_info->params.size; /* 0: write, 1: read */ dma_setup_params.transfer_direction = 1; dev_addr = ioctl(dma_info->dma_fd, DMA_MEMCPY_SETUP, &dma_setup_params); if (dev_addr != 0) { printf("can't get device addr read: 0x%x\n", dev_addr); return -1; } /* Start DMA */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_START, 0); if (ret != 0) { return -1; } dma_info->vir_addr = mmap(NULL, dma_info->params.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_info->dma_fd, 0); if (dma_info->vir_addr == MAP_FAILED) { printf("mmap failed\n"); return -1; } time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); while(!time2){ time2 = ioctl(dma_info->dma_fd, DMA_MEMCPY_GET_TIME, 0); } rate = ((dma_info->params.size * 1.0 / (time2 / 1000000.0)) / 1024 / 1024); neon_copy((unsigned char *)r_buffer, (unsigned char *)dma_info->vir_addr, dma_info->params.size); /* Verify data */ for(i = 0; i < dma_info->params.size ; i ++) { if(r_buffer[i] != w_buffer[i]) { dma_info->dma_trans_info.error_count++; if(dma_info->dma_trans_info.error_count == 1) { printf("\nData verify error at offset %d (0x%x), expect: 0x%x, actual: 0x%x\n", i, i * 2, w_buffer[i], r_buffer[i]); } } } dma_info->dma_trans_info.dma_r_cost_time = time2; dma_info->dma_trans_info.dma_r_rate = ((dma_info->params.size * 1.0 / (dma_info->dma_trans_info.dma_r_cost_time / 1000000.0)) / 1024 / 1024); error: /* Shutdown DMA transfer */ ret = ioctl(dma_info->dma_fd, DMA_MEMCPY_SHUTDOWN, 0); if (ret < 0) { status = -1; } munmap(dma_info->vir_addr, dma_info->params.size); return status; } static void dma_test(struct DMAInfo *dma_info) { int ret = -1; unsigned int num = 0; float total_dma_w_rate = 0, total_dma_r_rate = 0; float max_dma_w_rate = 0, max_dma_r_rate = 0; float min_dma_w_rate = -1, min_dma_r_rate = -1; uint32_t total_dma_w_time = 0, total_dma_r_time = 0; uint32_t max_dma_w_time = 0, max_dma_r_time = 0; uint32_t min_dma_w_time = -1, min_dma_r_time = -1; const uint32_t total_cycles = dma_info->params.cycles; printf("\nStarting DMA test with %d cycles...\n\n", total_cycles); // Initialize total error count uint32_t total_error_count = 0; while (dma_info->params.cycles-- > 0) { ret = dma_write(dma_info); if (ret < 0) { printf("dma_write return error code: %d\r\n\n\n", ret); break; } #if 1 printf("write end ---------\n"); continue; #endif printf("start read----\n"); ret = dma_read(dma_info); if (ret < 0) { printf("dma_read return error code: %d\r\n\n\n", ret); break; } num++; // Calculate error rate for this cycle float error_rate = (dma_info->dma_trans_info.error_count / (float)(dma_info->params.size / 2)) * 100; // Accumulate total error count total_error_count += dma_info->dma_trans_info.error_count; dma_info->dma_trans_info.error_count = 0; // Print current cycle statistics printf("\rCycle %d/%d - Data Size: %d bytes | Write: %.2f MB/s | Read: %.2f MB/s | Write Time: %d us | Read Time: %d us | Error Rate: %.2f%%", num, total_cycles, dma_info->params.size, dma_info->dma_trans_info.dma_w_rate, dma_info->dma_trans_info.dma_r_rate, dma_info->dma_trans_info.dma_w_cost_time, dma_info->dma_trans_info.dma_r_cost_time, error_rate); fflush(stdout); // Accumulate for write average calculation total_dma_w_rate += dma_info->dma_trans_info.dma_w_rate; // Calculate the write maximum value if (dma_info->dma_trans_info.dma_w_rate > max_dma_w_rate) { max_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; } // Calculate the write minimum value if (min_dma_w_rate == -1.0 || dma_info->dma_trans_info.dma_w_rate < min_dma_w_rate) { min_dma_w_rate = dma_info->dma_trans_info.dma_w_rate; } // Accumulate for read average calculation total_dma_r_rate += dma_info->dma_trans_info.dma_r_rate; // Calculate the write maximum value if (dma_info->dma_trans_info.dma_r_rate > max_dma_r_rate) { max_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; } // Calculate the write minimum value if (min_dma_r_rate == -1.0 || dma_info->dma_trans_info.dma_r_rate < min_dma_r_rate) { min_dma_r_rate = dma_info->dma_trans_info.dma_r_rate; } // Accumulated is used to calculate the average time to write total_dma_w_time += dma_info->dma_trans_info.dma_w_cost_time; // Write maximum time calculation if (dma_info->dma_trans_info.dma_w_cost_time > max_dma_w_time) { max_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; } // Write minimum time calculation if (min_dma_w_time == -1.0 || dma_info->dma_trans_info.dma_w_cost_time < min_dma_w_time) { min_dma_w_time = dma_info->dma_trans_info.dma_w_cost_time; } // Accumulated is used to calculate the average time to read total_dma_r_time += dma_info->dma_trans_info.dma_r_cost_time; // Read maximum time calculation if (dma_info->dma_trans_info.dma_r_cost_time > max_dma_r_time) { max_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; } // Read minimum time calculation if (min_dma_r_time == -1.0 || dma_info->dma_trans_info.dma_r_cost_time < min_dma_r_time) { min_dma_r_time = dma_info->dma_trans_info.dma_r_cost_time; } } if (num > 0) { // Calculate average error rate float average_error_rate = (total_error_count / (float)(num * (dma_info->params.size / 2))) * 100; // Print final statistics printf("\n\nTest Complete! Final Statistics:\n"); printf("Completed Cycles: %d/%d\n", num, total_cycles); printf("Data Size: %d bytes\n", dma_info->params.size); printf("Write Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_w_rate / num); printf(" Time: %d us (avg)\n", total_dma_w_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_w_rate); printf(" Time: %d us (min)\n", min_dma_w_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_w_rate); printf(" Time: %d us (max)\n", max_dma_w_time); printf("\nRead Performance:\n"); printf(" Rate: %.2f MB/s (avg)\n", total_dma_r_rate / num); printf(" Time: %d us (avg)\n", total_dma_r_time / num); printf(" Rate: %.2f MB/s (max)\n", max_dma_r_rate); printf(" Time: %d us (min)\n", min_dma_r_time); printf(" Rate: %.2f MB/s (min)\n", min_dma_r_rate); printf(" Time: %d us (max)\n", max_dma_r_time); printf("\nAverage Error Rate: %.2f%%\n", average_error_rate); } return; } int main(int argc, char *argv[]) { struct CmdLineParams params; struct DMAInfo dma_info; int ret = -1; if (parse_parameter(&params, argc, argv) == false) { printf("Please try --help to see usage.\n"); exit(1); } w_buffer = (uint8_t *)malloc(params.size); if (w_buffer == NULL) { printf("malloc w_buffer failed!\n"); exit(-1); } r_buffer = (uint8_t *)malloc(params.size); if (r_buffer == NULL) { free(w_buffer); printf("malloc r_buffer failed!\n"); exit(-1); } memset(&dma_info, 0, sizeof(struct DMAInfo)); dma_info.params.address = DSMC_MEM; dma_info.params.size = params.size; dma_info.params.cycles = params.cycles; ret = dma_init(&dma_info); if (ret < 0) { dma_destroy(&dma_info); return -1; } dma_test(&dma_info); dma_destroy(&dma_info); if (w_buffer != NULL) { free(w_buffer); } if (r_buffer != NULL) { free(r_buffer); } return 0; } 五 RTOS DMA例程/** * Copyright (c) 2024 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: Apache-2.0 ****************************************************************************** * @file dsmc_test.c * @version V1.0 * @brief dsmc test * * Change Logs: * Date Author Notes * 2024-10-10 Zhihuan He the first version * ****************************************************************************** */ #include <stdint.h> #include <rthw.h> #include <rtthread.h> #ifdef RT_USING_COMMON_TEST_DSMC #include "hal_base.h" #include "dma.h" #include "drv_dsmc_host.h" #define IO_BW_32 0 #define IO_BW_16 1 #define IO_BW_8 2 #define IO_TYPE_0 0 #define DMA_SIZE (0x10000) #define COUNTS 100 static struct rt_device *dma; static rt_sem_t mem_sem; static void m2m_complete(void *param) { rt_sem_release(mem_sem); } void dma_memcpy(int *dst_mem, int *src_mem, int size) { struct rt_dma_transfer *m2m_xfer; rt_err_t ret; int i, len; uint32_t tick_s, tick_e; /* ms */ m2m_xfer = (struct rt_dma_transfer *)rt_malloc(sizeof(*m2m_xfer)); mem_sem = rt_sem_create("memSem", 0, RT_IPC_FLAG_FIFO); RT_ASSERT(m2m_xfer != RT_NULL); RT_ASSERT(src_mem != RT_NULL); RT_ASSERT(dst_mem != RT_NULL); RT_ASSERT(mem_sem != RT_NULL); len = size / sizeof(int); for (i = 0; i < len; i++) src_mem[i] = len - i; rt_memset(dst_mem, 0x0, size); rt_memset(src_mem, 0x6, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src_mem, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst_mem, size); dma = rt_device_find("dmac0"); /* memcpy test */ rt_memset(m2m_xfer, 0x0, sizeof(*m2m_xfer)); m2m_xfer->direction = RT_DMA_MEM_TO_MEM; m2m_xfer->dst_addr = (rt_uint32_t)dst_mem; m2m_xfer->src_addr = (rt_uint32_t)src_mem; m2m_xfer->len = size; m2m_xfer->callback = m2m_complete; m2m_xfer->cparam = m2m_xfer; ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) { /* dma copy start */ ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_SINGLE_PREPARE, m2m_xfer); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_START, m2m_xfer); RT_ASSERT(ret == RT_EOK); /* wait for complete */ ret = rt_sem_take(mem_sem, RT_WAITING_FOREVER); RT_ASSERT(ret == RT_EOK); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_STOP, m2m_xfer); RT_ASSERT(ret == RT_EOK); } tick_e = HAL_GetTick(); ret = rt_memcmp(src_mem, dst_mem, size); rt_kprintf("dma memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); ret = rt_device_control(dma, RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL, m2m_xfer); RT_ASSERT(ret == RT_EOK); tick_s = HAL_GetTick(); for (i = 0; i < COUNTS; i++) rt_memcpy(dst_mem, src_mem, size); tick_e = HAL_GetTick(); ret = rt_memcmp(dst_mem, src_mem, size); rt_kprintf("cpu memcpy [%s]: avg: %7u MB/S with src: 0x%x dst: 0x%x len: %u counts: %u\n", ret ? "FAIL" : "PASS", size * COUNTS / (tick_e - tick_s) / 1000, src_mem, dst_mem, size, COUNTS); rt_sem_delete(mem_sem); rt_free(m2m_xfer); } static int dma_test_psram(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma test1: copy from ddr to psram\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("dma test2: copy from psram to ddr\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); rt_kprintf("dma test3: copy from psram to psram\n"); src = (int *)map->phys; dst = (int *)(map->phys + size); dma_memcpy(dst, src, size); rt_kprintf("DMA test done\n"); return 0; } void psram_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i, j; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t test_cap; uint32_t read, write; uint32_t test_data[] = {0x5aa5f00f, 0x0, 0xffffffff, 0x3cc3d22d}; rt_kprintf("start cs%d simple test\n", cs); for (j = 0; j < 4; j++) { test_cap = map->size; rt_kprintf("write, test_cap = 0x%x\n", test_cap); rt_kprintf(" write\n"); for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, test_data[j]); } rt_kprintf("read\n"); test_cap = map->size; rt_kprintf(" read\n"); for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); if (read != test_data[j]) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, test_data[j], test_data[j] ^ read); } } } rt_kprintf("phase 1: write address to data\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i); } for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 2: write 0x0f0f5aa5 + address\n"); test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->write(dsmc_host, cs, 0, i, map->phys + i + 0x0f0f5aa5); } test_cap = map->size; for (i = 0; i < test_cap; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); write = map->phys + i + 0x0f0f5aa5; if (read != write) { rt_kprintf("addr offset 0x%x: read = 0x%x, wr = 0x%x, error(0x%x)\n", i, read, write, write ^ read); } } rt_kprintf("phase 3: dma copy by software\n"); dma_test_psram(dsmc_host, cs); rt_kprintf("cs%d simple test done.\n", cs); } static int plc_simple_test(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; int *src = NULL, *dst = NULL; uint32_t size = DMA_SIZE; int ret = 0; uint32_t timeout = 1000000; rt_kprintf("phase 1: dma memcopy from host DDR to slave memory\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); /* init src address */ rt_memset(src, 0x66, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)src, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_to(dsmc_host, cs, 0, (uint32_t)src, 0x0, size)) { while (ops->copy_to_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy to complete timeout\n"); ret = -1; goto err; } } rt_memcmp(src, (int *)map->phys, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_kprintf("phase2 : dma memcopy from slave memory to host DDR\n"); dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); rt_memset(dst, 0x88, size); /* init plc src address */ for (i = 0; i < size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0x10100000 + i); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)dst, size); HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, size); timeout = 10000; if (!ops->copy_from(dsmc_host, cs, 0x0, 0, (uint32_t)dst, size)) { while (ops->copy_from_state(dsmc_host)) { rt_thread_mdelay(1); timeout--; if (!timeout) { rt_kprintf("DSMC: wait copy from complete timeout\n"); ret = -1; goto err; } } rt_memcmp((int *)map->phys, dst, size); } else { rt_kprintf("err: phase 1: test skip!!!\n"); } rt_dma_free(src); rt_dma_free(dst); rt_kprintf("plc_simple_test test done\n"); return 0; err: rt_kprintf("plc_simple_test test error!\n"); return ret; } void dsmc_slave_latency_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; uint32_t read, counter; uint32_t kt[2], diff; rt_kprintf("cpu access dsmc latency test\n"); counter = 1000000; kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->read(dsmc_host, cs, 3, 0x100, &read); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, read latency %uns\n", counter, diff, diff * 1000000 / counter); rt_kprintf("read = 0x%x\n", read); kt[0] = HAL_GetTick(); for (i = 0; i < counter; i++) { ops->write(dsmc_host, cs, 3, 0x100, 0x6); } kt[1] = HAL_GetTick(); diff = kt[1] - kt[0]; rt_kprintf("counter %d, cost %ums, write latency %uns\n", counter, diff, diff * 1000000 / counter); } void dsmc_slave_speed_cpu(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { uint32_t i; struct dsmc_host_ops *ops = dsmc_host->ops; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t kt[2], rd_diff, wr_diff; uint32_t read; rt_kprintf("cpu access dsmc speed\n"); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->write(dsmc_host, cs, 0, i, 0xffffffff); } HAL_DCACHE_CleanInvalidateByRange((uint32_t)map->phys, map->size); kt[1] = HAL_GetTick(); wr_diff = kt[1] - kt[0]; rt_kprintf("cpu write %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, wr_diff, map->size / wr_diff / 1000); kt[0] = HAL_GetTick(); for (i = 0; i < map->size; i += 4) { ops->read(dsmc_host, cs, 0, i, &read); } kt[1] = HAL_GetTick(); rd_diff = kt[1] - kt[0]; rt_kprintf("cpu read %uMB, cost %ums, %uMB/s\n", (uint32_t)map->size / 1024 / 1024, rd_diff, map->size / rd_diff / 1000); } static int dsmc_slave_speed_dma(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs) { int *src = NULL, *dst = NULL; struct DSMC_MAP *map = &dsmc_host->host->dsmcHostDev.ChipSelMap[cs].regionMap[0]; uint32_t size = DMA_SIZE; rt_kprintf("dma access dsmc speed\n"); rt_kprintf("DMA write DSMC\n"); src = rt_dma_malloc(size); RT_ASSERT(src != RT_NULL); dst = (int *)map->phys; dma_memcpy(dst, src, size); rt_dma_free(src); rt_kprintf("DMA read DSMC\n"); src = (int *)map->phys; dst = rt_dma_malloc(size); RT_ASSERT(dst != RT_NULL); dma_memcpy(dst, src, size); rt_dma_free(dst); return 0; } void dsmc_test(int argc, char **argv) { uint32_t i; rt_device_t dev; struct rockchip_rt_dsmc_host *dsmc_host; rt_kprintf("this is dsmc_test\n"); dev = rt_device_find("dsmc_host"); if (dev == RT_NULL) { rt_kprintf("dsmc_host not found\n"); return; } dsmc_host = (struct rockchip_rt_dsmc_host *)dev; for (i = 0; i < DSMC_MAX_SLAVE_NUM; i++) { if (dsmc_host->host->dsmcHostDev.ChipSelCfg[i].deviceType == DEV_UNKNOWN) continue; psram_simple_test(dsmc_host, i); if (dsmc_host->host->dsmcHostDev.ChipSelCfg[i].deviceType == DEV_DSMC_LB) { plc_simple_test(dsmc_host, i); dsmc_slave_latency_cpu(dsmc_host, i); } dsmc_slave_speed_cpu(dsmc_host, i); dsmc_slave_speed_dma(dsmc_host, i); } } #ifdef RT_USING_FINSH #include <finsh.h> MSH_CMD_EXPORT(dsmc_test, dsmc tester); #endif #endif
一 整体概述1.1 程序文件位置分布RTOS中DSMC代码，dsmc-controller.c是控制器初始化代码，dsmc-host.c是主机驱动代码 dsmc-lb-device.c是总线设备用户接口驱动程序，dsmc-lb-slave.c是从机程序 SDK/GA3506_Linux_Source/kernel-6.1/drivers/memory/rockchip/dsmc-controller.c SDK/GA3506_Linux_Source/kernel-6.1/drivers/memory/rockchip/dsmc-host.c SDK/GA3506_Linux_Source/kernel-6.1/drivers/memory/rockchip/dsmc-lb-device.c SDK/GA3506_Linux_Source/kernel-6.1/drivers/memory/rockchip/dsmc-lb-slave.c 1.2 四个文件整体之间的关系四个文件尽管分为控制器、主机、接口、从机四部分，但总体来说，从功能上来看只有主机和从机两个部分主机dsmc-host.c依赖dsmc-controller.c和dsmc-lb-device.c完成整体功能从机不依赖其他DSMC文件，只在dsmc-lb-slave.c里写寄存器配置DSMC，处理中断等主机端Host dsmc-host.c 注册 platform 驱动，probe 时会： 调用 dsmc-controller.c 的底层初始化接口rockchip_dsmc_ctrller_init，完成控制器硬件初始化 调用 dsmc-lb-device.c 注册 Local Bus 设备，创建 /dev/dsmc/... 字符设备节点，提供用户空间访问接口 注册 DMA 和中断，做好数据搬运准备 用户程序通过 /dev/dsmc/ 下的设备节点，发起 DMA 读写请求open/ioctl ioctl 处理，配置 DMA 参数，调用 dma_memcpy_init/dma_memcpy_trans_start 通过 dsmc-host.c 提供的 ops 接口，最终调用 dsmc-controller.c 的底层 DMA 函数发送出去从机端Slave dsmc-lb-slave.c 注册 platform 驱动，probe 时： 解析 dts，获取资源和内存映射 初始化寄存器，配置为 slave 模式 申请中断，等待主机端的中断操作通知 收到中断（如 S2H），进入中断服务函数 rockchip_dsmc_lb_slave_irq 调用 dsmc_lb_slave_dma_trigger()，触发本地 DMA 硬件搬运 搬运完成后清除中断，等待下一次操作二 控制器驱动代码解析2.1 控制器驱动初始化 dsmc-controller.cdsmc-controller.c主要负责 DSMC 控制器的底层硬件初始化、PSRAM检测与配置、延迟线训练、DMA 触发、寄存器操作等功能。它是整个 DSMC 总线主控端驱动的底层核心,直接操作硬件寄存器，完成对存储器和总线的初始化和管理。在RTOS中与其对应的是HAL_DSMC_HOST_Init中部分内容延迟线（DLL）训练：是为总线采样时钟增加适当延时，使得数据到达时间一致性主要结构体struct rockchip_dsmc：DSMC控制器的核心结构体，包含寄存器基址、配置参数等 struct dsmc_config_cs：描述每个chip select的配置信息，时序、宽度、延迟等 struct dsmc_map：映射的内存区域信息下列函数通过 EXPORT_SYMBOL 导出，可以被其他内核模块调用，比如dsmc-host.c等rockchip_dsmc_dll_training：延迟线训练 rockchip_dsmc_lb_dma_trigger_by_host：主控触发 DMA rockchip_dsmc_lb_dma_hw_mode_dis：关闭 DMA 硬件模式 rockchip_dsmc_ctrller_init：主控控制器初始化 rockchip_dsmc_lb_init：本地总线初始化 rockchip_dsmc_psram_reinit：PSRAM 重新初始化 rockchip_dsmc_device_dectect：设备检测大致调用流程：参考dsmc-host.c1调用rockchip_dsmc_ctrller_init初始化控制器 2调用rockchip_dsmc_device_dectect检测并识别PSRAM 3调用rockchip_dsmc_dll_training进行延迟线训练 4调用rockchip_dsmc_psram_reinit可重新初始化PSRAM参数三 主机驱动代码解析3.1 主机驱动初始化代码流向模块加载 → 驱动注册 → probe资源申请 → dts参数解析 → 控制器/设备初始化 → 内存映射 → DMA/中断注册 → 对外API注册 → 正常工作 → 释放资源 → 注销驱动 rk_dsmc_init -> platform_driver_register(&rk_dsmc_driver) -> rk_dsmc_probe -> dsmc_parse_dt -> dsmc_init -> dsmc_mem_remap ->rockchip_dsmc_dma_request -> priv->ops = &rockchip_dsmc_ops -> 正常工作（上层ops调用等）-> rk_dsmc_remove -> platform_driver_unregister(&rk_dsmc_driver)-> rk_dsmc_exit 3.2 主机驱动代码框架 结构体 struct rockchip_dsmc：主控结构体，包含寄存器映射、配置参数、DMA、中断等 struct dsmc_ctrl_config：控制器配置，包括各chip select的参数 struct dsmc_transfer：DMA传输相关参数与状态 rk_dsmc_init()：类与平台驱动注册，创建DSMC类，通常用于sysfs、热插拔等管理 platform_driver_register()：注册平台驱动，关联 .probe = rk_dsmc_probe，设备树匹配到 compatible 节点后会自动调用 probe rk_dsmc_probe()：平台驱动探测函数probe资源申请（malloc）,DMA申请与时钟初始化等 dsmc_parse_dt()：dts参数解析，读取slave/psram/lb-slave等节点参数，解析物理内存、时序、属性等 dsmc_init()：DSMC控制器初始化，内部会调用dsmc-controller.c里面的函数，实现控制器底层初始化 dsmc_mem_remap()：内存空间映射与设备注册，RTS可以直接访问物理地址，Linux下必须做虚拟地址映射 priv->ops = &rockchip_dsmc_ops：ops对外操作函数集注册，提供标准的 read、write、copy_from、copy_to 等接口给上层调用 用户获取DSMC设备指针，通过 ops 中的API进行寄存器读写、DMA数据搬运等操作 rk_dsmc_remove：关闭并释放时钟、DMA、内存映射、local bus设备等资源 rk_dsmc_exit：注销平台驱动，销毁DSMC类。 四 DSMC Local Bus（LB）设备驱动代码解析 dsmc-lb-device.c此文件负责DSMC控制器驱动 Local Bus 的字符设备节点注册、DMA 操作、用户接口的实现，由主驱动调用，用于实现用户空间访问和数据交换。4.1 驱动初始化代码流向rockchip_dsmc_lb_class_create / rockchip_dsmc_lb_class_destroy 创建/销毁 dsmc_class，用于 device_create。 rockchip_dsmc_register_lb_device / rockchip_dsmc_unregister_lb_device 注册/注销 Local Bus 字符设备节点，分配 cdev，生成 /dev/dsmc/csX/regionY 设备文件。 dsmc_fops：dma操作集 dsmc_ioctl：支持 DMA 配置、启动、获取时间、释放等命令。 dma_memcpy_init：申请并配置 DMA 通道 dma_memcpy_trans_start：提交 DMA 任务，等待完成 dma_cb_isr：DMA 传输完成回调 在 dsmc-host.c 或 dsmc-controller.c 里，初始化 Local Bus 设备时，会调用： rockchip_dsmc_lb_class_create()→ 创建 class rockchip_dsmc_register_lb_device()→ 为每个 cs/region 注册字符设备节点 卸载时调用： rockchip_dsmc_unregister_lb_device() rockchip_dsmc_lb_class_destroy()4.2 驱动代码调用细节主驱动dsmc-host.c在 probe 时调用rockchip_dsmc_lb_class_create()创建 class，用于后续设备节点挂载 rockchip_dsmc_register_lb_device(dev, cs)为每个 cs（chip select）下的每个 region 注册字符设备节点（/dev/dsmc/csX/regionY） 用户程序通过 /dev/dsmc/csX/regionY 设备节点进行操作如下： open("/dev/dsmc/csX/regionY", ...)//打开设备接口 → 内核调用 dsmc_open() 打开 dsmc 设备，分配DMA ioctl(fd, DMA_MEMCPY_SETUP, &params) 发送参数 → 内核调用 dsmc_ioctl() 从用户空间拷贝参数到 rw_dsmc_params 分配一块 DMA 一致性内存（dma_alloc_coherent） 设置 DMA 源/目的物理地址，虚拟地址等 调用 dma_memcpy_init() 申请并配置 DMA 通道 ioctl(fd, DMA_MEMCPY_START) → 内核调用 dsmc_ioctl(DMA_MEMCPY_START, ...) 调用 dma_memcpy_trans_start() 配置 DMA 描述符 设置回调（dma_cb_isr） 提交并启动 DMA 阻塞等待传输完成（wait_for_completion） ioctl(fd, DMA_MEMCPY_GET_TIME) → 内核调用 dsmc_ioctl(DMA_MEMCPY_GET_TIME, ...) 如果上次 DMA 完成，返回耗时（write_time） fd.close() → 内核调用 dsmc_release() 释放 DMA 结构体五 从机驱动代码解析dsmc-lb-slave.c此文件 是 DSMC Local Bus 的Slave端驱动，用于管理从设备的寄存器、时钟和总线协议相关配置主机端Host通过 DSMC 通道发起 DMA 操作从机端Slave收到中断后，触发硬件 DMA 拷贝，完成数据交互5.1 系统如何启动驱动？设备树匹配 → probe 初始化 → 寄存器初始化 → 申请中断 → 中断触发时，启动 DMA 传输 → 中断清除，等待下一次传输5.2 从机驱动初始化代码流向 struct rockchip_dsmc_lb_slave 保存了 slave 端的所有硬件资源指针和内存映射信息 module_platform_driver(rk_dsmc_lb_slave_driver)：注册 platform 驱动，驱动入口与设备匹配驱动入口与设备匹配 rk_dsmc_lb_slave_driver：指定 probe/remove、of_match_table rk_dsmc_lb_slave_probe()：分配并初始化 rockchip_dsmc_lb_slave 结构体 解析 dts 的 memory-region，获取 slave 端映射内存物理地址和大小 调用 dsmc_lb_slave_init() 做寄存器初始化，申请中断，绑定 rockchip_dsmc_lb_slave_irq dsmc_lb_slave_init()：寄存器初始化，使能slave模式 rockchip_dsmc_lb_slave_irq()：检查中断状态，调用 dsmc_lb_slave_dma_trigger()触发 DMA 传输，清除所有 H2S 中断 dsmc_lb_slave_dma_trigger()：检查 DMA 中断状态，等待清空，通过写寄存器触发 slave 到 host 的 DMA 硬件拷贝 rk_dsmc_lb_slave_remove()：关闭并释放时钟
1 Linux DTS DSMC在/home/linux/rk3506/GA3506_Linux_Source/kernel-6.1/arch/arm/boot/dts/GA3506_dsmc.dtsi中，将DSMC设备的status状态设备为okay，然后编译即可。2 RTOS DSMC**这里有说明如何将Linux设备树下设备如何共享给amp的rtos使用参考手册Rockchip_Developer_Guide_AMP_CN.pdf****首先找到/home/linux/rk3506/GA3506_Linux_Source/kernel-6.1/arch/arm/boot/dts/rk3506.dtsi文件在这个文件里有DSMC的中断编号，引脚等配置，首先将DSMC时钟加入到AMP的时钟配置内，然后把DSMC的引脚加入到AMP的引脚列表中，再把中断编号加上32的偏移放入AMP的中断配置内**然后在/home/linux/rk3506/GA3506_Linux_Source/rtos/bsp/rockchip/ga3506-32目录下打开终端，使用scons --menuconfig命令进入配置窗口，将DSMC HOST的打开并且将测试代码使能最后使用./build.sh命令整体编译打包烧录即可