#arm #nxp #imxrt #cortex-m #address-range

imxrt-dcd

适用于 i.MX RT1060 系列固件映像的设备配置数据 (DCD) 生成器

3 个稳定版本

1.1.0 2023 年 8 月 26 日
1.0.1 2023 年 8 月 22 日
1.0.0 2023 年 8 月 21 日
0.1.0 2023 年 8 月 18 日

#345 in 嵌入式开发

每月 35 次下载

MIT 许可证

39KB
563

i.MX RT1060 设备配置数据 (DCD) 生成器

Crates.io docs.rs

i.MX RT1050/1060 系列微控制器具有 ROM 启动加载程序。作为启动过程的一部分,它解释固件映像中的设备配置数据 (DCD) 部分,以执行有限的初始化和验证外设寄存器,例如设置外部存储器控制器,在执行固件映像中的任何 ARM 指令之前。

此 crate 定义

  • DCD 命令的语义描述符。
  • 将命令列表序列化为 DCD 二进制文件(字节数组)。

常见用例 / 工作流程

  • 在固件 crate 的 build.rs 脚本中,定义 DCD 命令并将它们序列化到文件中(例如 $OUT_DIR/dcd.bin)。
  • 在固件本身中,定义一个静态字节数组,用 DCD 二进制文件的内容初始化,可以将其链接到固件映像中。(无耻地宣传:static-include-bytes 有助于此步骤。)

DCD 究竟做什么?

固件映像中的 DCD 部分是一系列命令的序列化字节数组

  • 写入:向地址写入值(1/2/4 字节)。

    • *address = value —— 直接写入
    • *address &= !value —— 读取-修改-写入清除位
    • *address |= value —— 读取-修改-写入设置位
  • 检查:从地址读取,直到值满足条件或尝试次数过多。

    • (*address & mask) == 0 —— 全部清除
    • (*address & mask) == mask —— 全部设置
    • (*address & mask) != mask --- 任意清除
    • (*address & mask) != 0 --- 任意设置
  • NOP:忽略 —— 可能表现为短暂的延迟。

参考:i.MX RT1060 参考手册(版本 3),第 9.7.2 节。

以下为重要细节和注意事项,这些内容会影响对 DCD 的解释。

使用方法

use imxrt_dcd as dcd;
use imxrt_ral as ral;  // feature = "imxrt1062"

// RECOMMENDED: using imxrt-ral and convenience macros
let commands_macro = vec![
  dcd::write_reg!(ral::ccm_analog, CCM_ANALOG, PLL_ARM, @BYPASS, BYPASS_CLK_SRC: CLK1),
  dcd::check_all_clear!(ral::ccm, CCM, CDHIPR, @PERIPH_CLK_SEL_BUSY, @PERIPH2_CLK_SEL_BUSY),
];

// equivalent direct construction
let commands_direct = vec![
  dcd::Command::Write(dcd::Write {
      width: dcd::Width::B4,
      op: dcd::WriteOp::Write,
      address: 0x400D_8000,
      value: 0x0001_4000,
  }),
  dcd::Command::Check(dcd::Check {
    width: dcd::Width::B4,
    cond: dcd::CheckCond::AllClear,
    address: 0x400F_C048,
    mask: (1 << 3) | (1 << 5),
    count: None,
  }),
];

assert_eq!(commands_macro, commands_direct);

// `serialize` into an `std::io::Write`
let mut dcd_bytes = vec![];
let num_bytes_written = dcd::serialize(&mut dcd_bytes, &commands_macro).expect("IO error");
assert_eq!(num_bytes_written, 28);
assert_eq!(
  &dcd_bytes,
  &[
    // DCD header
    0xD2, 0, 28, 0x41,
    // write
    0xCC, 0, 12, 0x04, 0x40, 0x0D, 0x80, 0x00, 0x00, 0x01, 0x40, 0x00,
    // check
    0xCF, 0, 12, 0x04, 0x40, 0x0F, 0xC0, 0x48, 0x00, 0x00, 0x00, 0x28,
  ]
);

便捷宏

为了简化命令的构建,功能 "ral"(默认开启)提供了便捷宏,这些宏旨在与imxrt-ral中的寄存器定义一起使用。

这些宏具有以下共同语法:

macro!(ral::path::to::peripheral, INSTANCE, REGISTER, ...args)

其中

  • macro可以是

  • INSTANCE应为一个指向寄存器块的指针,例如对于 ral::ccm,这应该是 CCM

  • 每个 arg 可以是

    • FIELD: value => (value << field::offset) & field::mask
      • ral-registers 相同的行为。
      • 字段枚举/名称值可以直接在 value 表达式中使用。
    • @FIELD => FIELD::mask
      • 读取为 "所有(字段的)FIELD"
      • 对于显式使用字段掩码的设置、清除和检查命令非常有用。
    • 任意表达式
      • 可以直接引用寄存器的字段(例如 (0b110 << FIELD1::offset) | FIELD2::mask)。

然后所有参数都按位或在一起,作为命令的最终值/掩码。

此语法受到 imxrt-ral 中的 write_reg! 和其他友好的启发(重导出 ral-registers),并针对 DCD 的限制进行了调整。

DCD 详细信息和注意事项

DCD 大小限制

DCD序列化格式与头部中的2字节长度字段4字节对齐。这允许整个DCD块(包括所有头部)的最大长度为65532字节。然而,特定芯片系列的启动ROM可能强制实施(更短)的大小限制。对于RT1060,这是1768字节。

该存储库仅强制执行64 KiB长度限制以保持二进制格式有效,但会返回序列化DCD的大小,以便用户可以添加更严格的检查。

写入命令压缩

具有相同位宽和操作(即写入/清除/设置)的多个连续写入命令可以合并(共享相同的命令头部),以节省每个额外命令4字节。

该存储库在序列化期间自动执行此压缩。这可能会帮助满足DCD大小限制。

有效的写入命令地址范围

特定芯片系列的启动ROM可能只允许写入命令到有限数量的地址范围。

例如,以下是从参考手册中为RT1060的有效地址范围(再次)

开始 结束(包含) 描述
0x400A_4000 0x400A_7FFF IOMUX控制SNVS GPR
0x400A_8000 0x400A_BFFF IOMUX控制SNVS
0x400A_C000 0x400A_FFFF IOMUX控制GPR
0x401F_8000 0x401F_BFFF IOMUX控制
0x400D_8000 0x400D_BFFF CCM模拟
0x400F_C000 0x400F_FFFF CCM
0x402F_0000 0x402F_3FFF SEMC

将数据写入这些范围之外的任何位置将导致启动ROM立即放弃解释您剩余的DCD。

该存储库不强制执行任何地址范围限制。用户应提供针对目标芯片系列的有效写入地址。

检查命令轮询计数

检查命令可以指定以下之一

  • 省略最大轮询计数:只要条件未满足,ROM将无限期地轮询。
  • 最大轮询计数 == 0:根本不轮询——相当于NOP。
  • 最大轮询计数 > 0:如果达到最大轮询计数,启动ROM将立即放弃解释您剩余的DCD。

请注意(通过我有限的实验),启动ROM似乎没有限制检查命令的地址范围。

技巧

命令组合

  • ral::modify_reg!(..., FIELD1: value1, FIELD2: value2)可以使用以下DCD命令序列进行近似

    • dcd::clear_reg!(..., @FIELD1, @FIELD2)
    • dcd::set_reg!(..., FIELD1:value1, FIELD2:value2)
    • 注意:这可能会触发由两个读取-修改-写入周期引起的额外副作用。这是由于DCD中缺少临时变量的最小值。
  • ral::read_reg!(..., FIELD == value)可以使用以下DCD命令序列进行近似

    • dcd::check_all_set!(..., FIELD:value)
    • dcd::check_all_clear!(..., FIELD: !value)
    • 注意:如果寄存器在这两次检查之间发生变化,则此方法不起作用。可以使用相同的2个命令检查同一寄存器中的多个字段。

依赖关系

~425KB