返回所有文章
快速开始 / TSENGCHEN BLOG

RK3576_test1_FPGA_DSMC_Quick_Start

08_QuickStart · RK3576_test1_FPGA_DSMC_Quick_Start

08_QuickStart · 第 4 / 5 篇

保留原笔记正文与技术内容,仅脱敏敏感信息。配图按作者要求保留原样。

RK3576 与 Zynq DSMC 开发记录

项目概述

RK3576 TEST1 作为 DSMC Host,通过 LocalBus 与 Xilinx Zynq XC7Z020 PL 通信。 内核:Rockchip Linux 6.1 板型:RK3576 TEST1 V10 片选:CS0 位宽:x16 频率:25 MHz region0:Merged FIFO,0x10000000 region3:Register,0x12000000

设备树

板级配置文件:

/home/user/kernel-6.1/arch/arm64/boot/dts/rockchip/rk3576-test1.dtsi

配置:

&dsmc {
    clock-frequency = <25000000>;
    status = "okay";
};

&dsmc_slave {
    rockchip,clk-mode = <0>;
    status = "okay";

    lb-slave {
        rockchip,io-width = <16>;
    };
};

&dsmc_lb_slave0 {
    rockchip,mtr-timing = <1 0 0 0 0 0 2 2>;
    rockchip,int-en = <0>;
    status = "okay";
};

&dsmc_p0_region0 {
    status = "okay";
};

&dsmc_p0_region2 {
    status = "disabled";
};

&dsmc_p0_region3 {
    status = "okay";
};

内核配置保持:

CONFIG_ROCKCHIP_DSMC=y

CONFIG_ROCKCHIP_DSMC_SLAVE is not set

DSMC不同模式的启停

开启/停止DSMC

/* 全局启用 DSMC */
  &dsmc {
      status = "okay";
  };

  &dsmc_slave {
      status = "okay";
  };

选择使用PSRAM CSx/ LocalBus CSx

/* 选择 PSRAM CS0  选择哪个就把哪个打开就好*/
  &dsmc_psram0 { status = "okay"; };
  &dsmc_psram1 { status = "disabled"; };
  &dsmc_psram2 { status = "disabled"; };
  &dsmc_psram3 { status = "disabled"; };

  &dsmc_lb_slave0 { status = "disabled"; };
  &dsmc_lb_slave1 { status = "disabled"; };
  &dsmc_lb_slave2 { status = "disabled"; };
  &dsmc_lb_slave3 { status = "disabled"; };

选择LocalBus 位宽

/* Local Bus 位宽 */
  &dsmc_slave {
      lb-slave {
          rockchip,io-width = <8>;   /* x8 */
          /* rockchip,io-width = <16>; */ /* x16 */
      };
  };

选择Regionx 和 数据传输模式

  /* CS0:region0,寄存器窗口 */
  &dsmc_p0_region0 {
      status = "okay";
      rockchip,attribute = "Register";
  };
  
  /* CS0:region1,合并 FIFO 数据窗口 */
  &dsmc_p0_region1 {
      status = "okay";
      rockchip,attribute = "Merged FIFO";
  };

  /* CS0:region2,不合并 FIFO 数据窗口 */
  &dsmc_p0_region2 {
      status = "okay";
      rockchip,attribute = "No-Merge FIFO";
  };

  /* CS0:region3,双口 RAM 窗口 */
  &dsmc_p0_region3 {
      status = "okay";
      rockchip,attribute = "DPRA";
  };

不同region 传输数据

  • “Register”:控制、状态、配置寄存器。
  • “Merged FIFO”:连续数据 FIFO,允许合并访问,适合高吞吐数据流。
  • “No-Merge FIFO”:不合并每次读写,适合读写有副作用的 FIFO。
  • “DPRA”:双口 RAM,适合共享内存窗口。

选择原则:寄存器用 Register;普通连续数据通道优先 Merged FIFO;每一次访问都必须独立生效时用 No-Merge FIFO; FPGA 暴露共享 RAM 时用 DPRA。

Patch

3.1 RK3576 测试适配

/home/user/tools/linux/0002-WIP-add-dsmc-test-rk3576-adapt.patch

作用:添加 dsmc-test.c、dsmc-memtester.c 和 sysfs 测试入口,并移除原 patch 中仅适用于 RK3506、强制 x8 等修改。

3.2 DMA 修复

/home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

作用:修复 kernel 6.1 中 dma_interleaved_template.sgl[] 柔性数组没有分配空间导致的内核栈越界。运行 plc_test 前必须应用此 patch。

修改文件:

/home/user/kernel-6.1/drivers/memory/rockchip/dsmc-host.c

需要修改三个位置。

1. 头文件区域

位置约在文件第 5~16 行。

增加:

#include <linux/slab.h>

放在:

#include <linux/reset.h>

#include <linux/slab.h>

2. DMA发送函数

函数位置约在第629行:

static int rockchip_dsmc_lb_prepare_tx_dma(
        struct device *dev,
        struct rockchip_dsmc *dsmc,
        uint32_t cs)

将:

struct dma_interleaved_template xt;

修改为:

struct dma_interleaved_template *xt;

将:

memset(&xt, 0, sizeof(xt));

修改:

xt = kzalloc(struct_size(xt, sgl, 1), GFP_KERNEL);
  if (!xt)
        return -ENOMEM;

将:

dmaengine_config_interleaved(xfer, &xt, DMA_MEM_TO_DEV, 1);
  txdesc = dmaengine_prep_interleaved_dma(dsmc->dma_req[cs], &xt,
                                        flags | DMA_PREP_INTERRUPT);

修改为:

dmaengine_config_interleaved(xfer, xt, DMA_MEM_TO_DEV, 1);
  txdesc = dmaengine_prep_interleaved_dma(dsmc->dma_req[cs], xt,
                                        flags | DMA_PREP_INTERRUPT);

kfree(xt);

3. DMA接收函数

函数位置约在第666行:

static int rockchip_dsmc_lb_prepare_rx_dma(
        struct device *dev,
        struct rockchip_dsmc *dsmc,
        uint32_t cs)

同样将:

struct dma_interleaved_template xt;

修改为:

struct dma_interleaved_template *xt;

将:

memset(&xt, 0, sizeof(xt));

修改为:

xt = kzalloc(struct_size(xt, sgl, 1), GFP_KERNEL);
  if (!xt)
        return -ENOMEM;

将:

dmaengine_config_interleaved(xfer, &xt, DMA_DEV_TO_MEM, 1);
  rxdesc = dmaengine_prep_interleaved_dma(dsmc->dma_req[cs], &xt,
                                        flags | DMA_PREP_INTERRUPT);

修改为:

dmaengine_config_interleaved(xfer, xt, DMA_DEV_TO_MEM, 1);
  rxdesc = dmaengine_prep_interleaved_dma(dsmc->dma_req[cs], xt,
                                        flags | DMA_PREP_INTERRUPT);
  kfree(xt);

完整修复 patch:

/home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

应用方法:

cd /home/user/kernel-6.1

git apply --check \

/home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

git apply \

/home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

从干净源码应用:

cd /home/user/kernel-6.1

git apply --check /home/user/tools/linux/0002-WIP-add-dsmc-test-rk3576-adapt.patch

git apply /home/user/tools/linux/0002-WIP-add-dsmc-test-rk3576-adapt.patch

git apply --check /home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

git apply /home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

git diff —check

编译

cd /home/user

./build.sh rockchip_rk3576_test1_v10_defconfig

./build.sh kernel

产物:

/home/user/kernel-6.1/boot.img

检查 DTB:

DTB=/home/user/kernel-6.1/arch/arm64/boot/dts/rockchip/rk3576-test1-v10-linux.dtb

fdtget "$DTB" /dsmc@2a280000 status

fdtget -t u "$DTB" /dsmc@2a280000 clock-frequency

fdtget "$DTB" /dsmc-slave/lb-slave/lb-slave0/region/region0 status

fdtget "$DTB" /dsmc-slave/lb-slave/lb-slave0/region/region3 status

预期为 okay、25000000、okay、okay。

板端验证

FPGA 必须在 RK3576 DSMC probe 前加载 bitstream 并 ready。

dmesg | grep -i dsmc

test -L /sys/bus/platform/drivers/dsmc/2a280000.dsmc \

&& echo "DSMC绑定成功" \

|| echo "DSMC绑定失败"

成功日志应包含:

cs0 byte0: DLL window [...]

cs0 byte1: DLL window [...]

CPU 回环

ADDR=0x10001000

OLD=$(devmem $ADDR 32)

devmem $ADDR 32 0x55aa1234

devmem $ADDR 32

devmem $ADDR 32 0xaa55abcd

devmem $ADDR 32

devmem $ADDR 32 “$OLD”

实测读回:

0x55AA1234

0xAA55ABCD

DMA 回环

确认已应用 0003 且 region0 允许覆盖至少 4 MiB 后:

cat /sys/kernel/rk_dsmc_test/plc_test

dmesg | tail -n 200

成功应包含:

cpu_compare_test test done

plc_simple_test test done

Pasted image 20260715150044

Debug

DSMC 未启用

现象:

  • dmesg 中没有 DSMC 日志

  • /sys/bus/platform/devices/ 中没有 2a280000.dsmc

    检查命令:

    tr '\0' '\n' < /proc/device-tree/dsmc@2a280000/status

    如果输出 disabled,说明当前 DTB 没有启用 DSMC。需要检查:

  • 是否修改了 rk3576-test1.dtsi

  • 是否重新编译了内核

  • 是否烧写了最新的 boot.img

    DLL 训练失败

    DSMC: cs0 byte0 dll training failed

    DSMC dll training fail

    含义:

    驱动扫描了 DLL 延迟值 0x00~0xff,但所有延迟值都无法正确完成数据写入和读回。

    检查命令:

    dmesg | grep -i dsmc

    重点检查:

  • FPGA 是否已经加载 bitstream

  • FPGA LocalBus Slave 是否 ready

  • DQ[7:0] 和 DQS0 是否正常

  • DQ[15:8] 和 DQS1 是否正常

  • CSN0、CLKP/CLKN、RDYN 是否正常

  • ARM 与 FPGA 的 x8/x16 配置是否一致

  • FPGA 是否实现对应 region

  • region 前 0x100 字节是否支持写入并原样读回

  • clk-mode 和 mtr-timing 是否与 FPGA 一致

    如果只报 byte0 失败,不一定只是 DQS0 问题。驱动遇到 byte0 失败后会直接停止,因此整个 FPGA 读返回异常也会表现为 byte0 失败。

    DLL 训练成功

    正常日志示例:

    cs0 byte0: DLL window [0x20-0xff] length=0xe0, current=0x8f

    检查驱动是否绑定:

    test -L /sys/bus/platform/drivers/dsmc/2a280000.dsmc && echo "DSMC绑定成功" || echo "DSMC绑定失败"

    必须同时满足:

  • byte0 DLL 训练成功

  • byte1 DLL 训练成功

  • DSMC platform driver 绑定成功

    注意:

    /dev/dsmc/cs0/region0 等节点存在,不代表驱动一定成功。旧驱动在 probe 失败后可能残留字符设备节点。

    Asynchronous SError

    错误日志:

    Kernel panic - not syncing: Asynchronous SError Interrupt

    含义:

    ARM 访问 DSMC 外部地址时,FPGA 没有返回有效总线响应。

    常见原因:

  • FPGA 尚未 ready

  • DSMC probe 已失败,控制器时钟被关闭

  • 访问了错误的 region 地址

  • FPGA 没有实现对应 region

  • DQS 或 RDYN 没有正确返回

  • FPGA 在通信过程中复位或丢失 bitstream

    发生后不要继续执行 devmem。

    先检查:

    dmesg | grep -i dsmc

    test -L /sys/bus/platform/drivers/dsmc/2a280000.dsmc && echo "DSMC成功" || echo "DSMC失败"

    冷启动阶段 external abort

    异常调用路径:

    rk_dsmc_probe

    → rockchip_dsmc_dll_training

    → dsmc_dll_training_method

    → dsmc_read

    → __raw_readl

    含义:

    RK3576 在启动约 2.5 秒时开始 DSMC DLL 训练,但 FPGA 可能还没有完成 bitstream 加载和 LocalBus 初始化。

    判断方法:

  1. 保持 FPGA 上电并 ready

  2. 只重启 RK3576

  3. 如果热重启成功、冷启动失败,基本可以确认是启动时序问题

    建议方案:

  • 最可靠:增加 FPGA Ready GPIO

  • DSMC 驱动检测到 Ready 后再初始化

  • 临时验证可以增加固定延时

  • 固定延时不适合作为最终方案

    DMA 测试导致内核栈破坏

    错误日志:

    stack-protector: Kernel stack is corrupted in: rockchip_dsmc_lb_prepare_tx_dma

    调用路径:

    plc_test_show

    → plc_simple_test

    → dsmc_copy_to

    → rockchip_dsmc_lb_prepare_tx_dma

    原因:

    kernel 6.1 中 dma_interleaved_template 的最后一个成员是柔性数组:

    struct data_chunk sgl[];

    旧代码只在栈上定义结构本体,但继续访问 sgl[0],造成内核栈越界,最终触发 Kernel Panic。

    修复 patch:

    /home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

    该 patch 同时修复:

  • DMA TX 模板

  • DMA RX 模板

  • 柔性数组空间分配

  • 使用完成后的内存释放

    应用命令:

    cd /home/user/kernel-6.1

    git apply --check /home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

    git apply /home/user/tools/linux/0003-fix-rk3576-dsmc-dma-template.patch

    RCU Stall

    错误日志:

    rcu_sched detected stalls

    CPU7 ... rockchip_panic_notify

    含义:

    RCU stall 通常是前面 Kernel Panic 或 external abort 的后续表现,不是独立的 DSMC 故障。

    某个 CPU 已经卡在 panic 处理流程,其他 CPU 等待 RCU 同步,因此持续打印 stall。

    排查时应向前查找最早出现的日志:

- Asynchronous SError

- external abort

- stack\-protector

- Kernel panic
不要从 RCU stall 本身开始排查。

CPU 回环测试

前提:

  • FPGA 已 ready

  • DLL 训练成功

  • DSMC 驱动绑定成功

  • 测试地址允许覆盖

    命令:

    ADDR=0x10001000

    OLD=$(devmem $ADDR 32)

    devmem $ADDR 32 0x55aa1234 devmem $ADDR 32 0xaa55abcd

    devmem $ADDR 32 “$OLD"

    0x55AA1234

    0xAA55ABCD

    结论:

  • ARM → FPGA 写通路正常

  • FPGA → ARM 读通路正常

  • region0 地址译码正常

  • DQ/DQS 基本通信正常

常用命令

检查内核版本:

uname -a

检查 DSMC 日志:

dmesg | grep -i dsmc

检查全部相关异常:

dmesg | grep -i -E 'dsmc|dll|serror|abort|stack|panic|rcu'

检查驱动绑定:

test -L /sys/bus/platform/drivers/dsmc/2a280000.dsmc && echo "DSMC绑定成功" || echo "DSMC绑定失败"

检查设备节点:

find /dev/dsmc -type c 2>/dev/null

检查运行时设备树:

tr '\0' '\n' < /proc/device-tree/dsmc@2a280000/status

08_QuickStart · 文档目录
上一篇RK3576_CAN_Quick_Start下一篇RK3588_RK1828_Pcie_LLM_Quick_Start