找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4771

积分

0

好友

653

主题
发表于 5 小时前 | 查看: 4| 回复: 0

在工业控制和数据采集场景中,处理器与 FPGA 之间的高速数据交换是一个常见需求。传统方案通常采用 SPI(速度受限,一般 < 10Mbps)或 PCIE(带宽高但引脚多、设计复杂)。有没有一种方案,既能达到几十 MB/s 的带宽,又只需要少量引脚,设计简单?

今天分享一个基于睿擎派 RC3506 的方案——利用 RK3506 芯片内置的 DSMC(Double Data Rate Serial Memory Controller)总线,与 PGL22GB FPGA 实现高速通信,完整演示从硬件连接到代码验证的全流程。如果你也在寻找高效简洁的处理器与FPGA通信方案,欢迎到云栈社区的技术板块交流探讨。

一、DSMC 是什么?

DSMC(Double Data Rate Serial Memory Controller)是瑞芯微 SoC 内部的一种高速存储与本地总线控制器,支持通过 DDR(双倍数据速率)方式与外部 PSRAM 或 LocalBus 设备(如 FPGA)进行数据通信。

DSMC 核心特性

DSMC 双倍数据速率控制器特性说明表

二、硬件准备

硬件准备清单:RC3506开发板、PGL22GB FPGA扩展板、调试工具

连接方式

RC-PI-3506 开发板与 FPGA 扩展板通过 DSMC 接口连接:

睿擎派 RC3506 与 PGL22GB FPGA 扩展板连接示意

睿擎派 RC3506 与 PGL22GB FPGA 扩展板连接示意

睿擎派 RC3506 开发板

睿擎派 RC3506 开发板

三、DSMC 读写原理

3.1 内存映射

DSMC 将外部设备映射到处理器的物理地址空间,软件可以通过直接读写内存地址的方式访问 FPGA:

处理器地址空间
    │
    ├── 0xC0000000 ──→ DSMC CS0 区域(FPGA RAM)
    │                   16KB 可读写
    │
    └── 其他外设...

3.2 读写流程

写入流程

1. 处理器执行 writel(data, 0xC0000000 + offset)
2. DSMC 控制器将数据通过 DDR 串行总线发送给 FPGA
3. FPGA LocalBus 接口接收数据,写入内部 RAM

读取流程

1. 处理器执行 readl(0xC0000000 + offset)
2. DSMC 控制器向 FPGA 发送读请求
3. FPGA LocalBus 接口从内部 RAM 读出数据,返回给 DSMC
4. DSMC 控制器将数据返回给处理器

3.3 关键约束

4字节对齐访问:RK3506 的 DSMC 读写必须 4 字节对齐,即地址必须是 4 的倍数

默认频率 12MHz:可在设备树中修改,但需确保 FPGA 侧时序匹配

8 位宽模式:当前示例使用 8 线传输,可配置为 16 线提升带宽

四、代码实战

4.1 示例代码结构

完整示例代码位于 SDK 的 02_peripheral_dsmc 目录,核心逻辑如下:

static int test_dsmc_read_write(void)
{
    int i;
    int err_count = 0;
    rt_uint64_t t_start, t_end;
    rt_uint64_t write_cycles, read_cycles;
    const int WORD_NUM = 4 * 1024;         // 4K word = 16KB
    uintptr_t BASE_ADDR = 0xC0000000;      // DSMC CS0 基地址
    rt_uint32_t *write_buf = rt_malloc(WORD_NUM * 4);
    rt_uint32_t *read_buf  = rt_malloc(WORD_NUM * 4);
    if (!write_buf || !read_buf)
    {
        rt_kprintf("malloc failed!\n");
        return -1;
    }
    // 准备测试数据
    for (i = 0; i < WORD_NUM; i++)
    {
        write_buf[i] = 0x12345678 + i;
    }
    // ========== 写入测试 ==========
    t_start = rt_hw_global_timer_get();
    for (i = 0; i < WORD_NUM; i++)
    {
        writel(write_buf[i], BASE_ADDR + i * 4); // 4字节对齐写入
    }
    t_end = rt_hw_global_timer_get();
    write_cycles = t_end - t_start;
    // ========== 读取测试 ==========
    t_start = rt_hw_global_timer_get();
    for (i = 0; i < WORD_NUM; i++)
    {
        read_buf[i] = readl(BASE_ADDR + i * 4);
    }
    t_end = rt_hw_global_timer_get();
    read_cycles = t_end - t_start;
    // ========== 数据校验 ==========
    for (i = 0; i < WORD_NUM; i++)
    {
        if (read_buf[i] != write_buf[i])
        {
            err_count++;
            rt_kprintf("ERR[%d]: R=0x%08X W=0x%08X\n",
                        i, read_buf[i], write_buf[i]);
            if (err_count > 10)
            {
                rt_kprintf("......\n");
                break;
            }
        }
    }
    // ========== 性能统计 ==========
    float write_time_us = (float)write_cycles / 24.0f;  // 24MHz 定时器
    float read_time_us  = (float)read_cycles  / 24.0f;
    float total_bytes = WORD_NUM * 4.0f;
    float write_speed = (total_bytes / (1024 * 1024)) / (write_time_us / 1e6);
    float read_speed  = (total_bytes / (1024 * 1024)) / (read_time_us  / 1e6);
    if (err_count == 0)
    {
        rt_kprintf("\n==== DSMC TEST PASS ====\n");
        rt_kprintf("Write: %lu cycles, %.2f us, %.2f MB/s\n",
                    write_cycles, write_time_us, write_speed);
        rt_kprintf("Read : %lu cycles, %.2f us, %.2f MB/s\n",
                    read_cycles, read_time_us, read_speed);
    }
    else
    {
        rt_kprintf("\n==== DSMC TEST FAIL ====\n");
        rt_kprintf("Error count: %d\n", err_count);
    }
    rt_free(write_buf);
    rt_free(read_buf);
    return 0;
}
MSH_CMD_EXPORT(test_dsmc_read_write, test dsmc read write);

4.2 关键代码解析

① 基地址定义

uintptr_t BASE_ADDR = 0xC0000000;  // DSMC CS0 区域

DSMC 的 CS0 片选对应的物理地址是 0xC0000000,软件直接读写该地址即可访问 FPGA。

② 4 字节对齐访问

writel(write_buf[i], BASE_ADDR + i * 4);  // 每次写入 4 字节
read_buf[i] = readl(BASE_ADDR + i * 4);   // 每次读取 4 字节

RK3506 的 DSMC 控制器要求访问地址 4 字节对齐,因此使用 writel/readl 函数,偏移量每次增加 4。

③ 性能测量

t_start = rt_hw_global_timer_get();  // 获取硬件定时器计数值
// ... 执行写入或读取 ...
t_end = rt_hw_global_timer_get();
cycles = t_end - t_start;
time_us = cycles / 24.0f;  // 定时器频率 24MHz

通过硬件定时器精确测量读写耗时,计算出实际带宽。

五、运行结果

将示例工程编译下载到睿擎派 RC3506,在串口终端执行:

msh />test_dsmc_read_write

输出结果

==== DSMC TEST PASS ====
Write: 73640 cycles, 3068.33 us, 5.09 MB/s
Read : 240044 cycles, 10001.83 us, 1.56 MB/s

结果解读

指标 写入 读取
数据量 16KB 16KB
耗时 3.07 ms 10.00 ms
带宽 5.09 MB/s 1.56 MB/s
校验结果 ✅ 全部通过 ✅ 全部通过

写入带宽达到 5MB/s,读取带宽 1.5MB/s,通过调整demo的时序和位宽,以及dma的加持,带宽可以达到 200MB/s。

六、FPGA 工程说明

6.1 FPGA 实现的功能

FPGA 侧实现了一个 LocalBus Server,功能如下:

  • 接收 DSMC 发来的写请求,将数据存入 16KB 内部 RAM
  • 响应 DSMC 的读请求,从 RAM 中读出数据返回
  • 提供状态 LED 指示通信活动

6.2 FPGA 工程迁移

如果需要将 FPGA 工程迁移到不同版本的 IDE,步骤如下:

  1. 在目标 IDE 上创建 PGL22GB-6MBG324 工程
  2. 添加源文件:dsmc.fdc、dsmc_port.v、led.v
  3. 配置 RAM 和 PLL IP 核

RAM IP 核配置

PGL22GB RAM IP 核配置:16KB 容量,32 位数据宽度

PLL IP 核配置

PGL22GB PLL IP 核配置:生成 DSMC 所需时钟

七、设备树配置

DSMC 的引脚复用和时钟频率在设备树中配置:

&dsmc {
    status = "okay";
    clock-frequency = <12000000>;  /* 12MHz,可修改 */

    pinctrl-names = "default";
    pinctrl-0 = <&dsmc_pins>;

    dsmc_pins: dsmc-pins {
        pins = "GPIO0_A0", "GPIO0_A1", "GPIO0_A2", "GPIO0_A3",
               "GPIO0_A4", "GPIO0_A5", "GPIO0_A6", "GPIO0_A7",
               "GPIO0_B0", "GPIO0_B1";
        function = "dsmc";
    };
};

注意事项

  • 修改 clock-frequency 后,需同步修改 FPGA 侧 PLL 配置
  • 引脚复用配置需与硬件原理图对应
  • 当前示例使用 CS0,如需使用其他片选需修改设备树

八、工程实现说明

  • 当前 DSMC 为 8 位宽模式,开启 DSMC 的 CS0 的 FIFO region
  • 默认频率为 12MHz,在示例工程的 board.dts 中可修改
  • 除 DSMC 的引脚复用与时钟,其他配置暂时不支持修改
  • DSMC 的 DMA 以及中断暂时不支持开启

九、应用场景

DSMC 与 FPGA 高速通信的典型应用场景:

场景 说明
高速 ADC 采集 FPGA 采集高速 ADC 数据,通过 DSMC 传输给处理器
电机控制 FPGA 实现多路 PWM 和编码器接口,处理器下发控制参数
视频预处理 FPGA 做 ISP 前处理,通过 DSMC 传输给处理器编码
协险品检测 FPGA 实现多传感器并行采集,处理器做 AI 推理

十、总结

这篇文章完整演示了睿擎派 RC3506 通过 DSMC 总线与 FPGA 实现高速通信:

✅ 理解 DSMC 的工作原理和内存映射机制
✅ 掌握 DSMC 与 FPGA 的硬件连接方式
✅ 学会使用 writel/readl 直接读写 DSMC 地址空间
✅ 完成 16KB 数据的读写测试和校验
✅ 了解 FPGA 工程的配置和迁移方法

DSMC 为处理器与 FPGA 之间提供了一种带宽适中、设计简单、引脚可控的高速通信方案,非常适合工业控制和数据采集场景。这类软硬协同的嵌入式开发实践,对于构建高性能实时系统至关重要。

示例工程

  • SDK 新建示例工程:02_peripheral_dsmc



上一篇:11个AI Agent如何帮助我管理四个孩子的家庭学习?一位开发者的自动化实践
下一篇:如何用Flink CDC与StarRocks构建轻量级实时数仓方案
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-19 09:37 , Processed in 1.495141 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表