Zephyr CRC 子系统

概述 链接到标题

Zephyr 的 CRC 子系统为数据完整性验证提供丰富的 CRC 算法软件实现,并支持硬件加速。可通过简单的 API 调用计算 CRC,也可以使用 “update” 函数处理流数据。本文从 CRC 子系统实现架构角度,介绍其实现原理和机制,不涉及实际的 CRC 软件算法和硬件驱动分析。

CRC 算法支持 链接到标题

Zephyr CRC 子系统对外的 API 在 zephyr/include/zephyr/sys/crc.henum crc_type 描述了其支持的 CRC 类型,每种类型的作用和对应的 crc 函数如下:

  • CRC4:4 位 CRC 算法(参考 crc4),常用于极短数据字段或私有嵌入式协议。
  • CRC4_TI:TI 变体的 4 位 CRC 算法(参考 crc4_ti),常用于 TI 芯片相关通信或控制协议。
  • CRC7_BE:大端模式的 7 位 CRC 算法(参考 crc7_be),常用于 SD / MMC 卡命令帧校验。
  • CRC8:8 位 CRC 算法(参考 crc8),常用于传感器、I²C / SPI 等简单通信。
  • CRC8_CCITT:CCITT 标准的 8 位 CRC 算法(参考 crc8_ccitt),常用于通信协议和嵌入式链路层。
  • CRC8_ROHC:ROHC 标准的 8 位 CRC 算法(参考 crc8_rohc),常用于蜂窝网络中的 IP 头压缩。
  • CRC16:16 位 CRC 算法(参考 crc16),常用于工业控制和通用数据校验。
  • CRC16_ANSI:ANSI 标准的 16 位 CRC 算法(参考 crc16_ansi),常用于 Modbus 等工业现场总线。
  • CRC16_CCITT:CCITT 标准的 16 位 CRC 算法(参考 crc16_ccitt),常用于电信系统和数据链路层。
  • CRC16_ITU_T:ITU-T 标准的 16 位 CRC 算法(参考 crc16_itu_t),常用于 HDLC、串行通信协议。
  • CRC24_PGP:PGP 标准的 24 位 CRC 算法(参考 crc24_pgp),常用于 OpenPGP 消息完整性校验。
  • CRC32_C:Castagnoli 多项式的 32 位 CRC 算法(参考 crc32_c),常用于存储、网络和内核数据校验。
  • CRC32_IEEE:IEEE 标准的 32 位 CRC 算法(参考 crc32_ieee),常用于以太网、文件格式和压缩数据。
  • CRC32_K_4_2:CRC32-C(Koopman)更新算法的 32 位 CRC(参考 crc32_k_4_2_update),常用于支持 SSE4.2 的高性能网络、存储和内核路径。

实现架构 链接到标题

Zephyr 有完善的 CRC 软件实现,当硬件支持 CRC 时,会优先使用硬件加速。其架构如下:

crc_arch

本文以 Zephyr CRC 子系统实现架构为主,CRC 的软件实现是多项式算法,这类技术基础不在本文介绍范围内。

核心函数 链接到标题

zephyr/include/zephyr/sys/crc.h 中声明了所有 CRC 子系统的函数,除了上一节列出的函数外还有 crc32_ieee_updatecrc24_pgp_update 两个函数,用于处理流数据。

另外提供一个函数 crc_by_type 会根据 enum crc_type 选择执行不同的 crc 函数。

软件实现 链接到标题

zephyr/subsys/crc 下提供这些算法函数的软件实现:

  • crc24_sw.c
  • crc32k_4_2_sw.c
  • crc4_sw.c
  • crc8_sw.c
  • crc16_sw.c
  • crc32c_sw.c
  • crc32_sw.c
  • crc7_sw.c

硬件加速机制 链接到标题

在有硬件 CRC 时,硬件 CRC 的函数会覆盖软件 CRC 的,这通过弱化软件实现 crc 算法函数的符号来实现,例如:

uint8_t __weak crc7_be(uint8_t seed, const uint8_t *src, size_t len)

以上软件实现 crc 的函数都用 __weak 进行修饰。

硬件实现 链接到标题

zephyr/subsys/crc/crc_hardware.c 中通过调用 crc 驱动实现相应的 crc 算法函数,例如:

uint8_t crc7_be(uint8_t seed, const uint8_t *src, size_t len)
{
	int ret;

	struct crc_ctx ctx = {
		.type = CRC7_BE,
		.polynomial = CRC7_BE_POLY,
		.seed = seed,
		.reversed = 0,
	};

	ret = crc_operation(crc_dev, &ctx, src, len);
	if (ret != 0) {
		__ASSERT_MSG_INFO("CRC operation failed: %d", ret);
		return 0;
	}

	return ctx.result & 0x7F;
}

当对应的硬件实现函数被编译时,软件算法的弱符号将不会被链接使用。

CRC 硬件驱动 链接到标题

CRC 驱动接口 链接到标题

crc_operation 是通过 crc driver 标准接口 zephyr/include/zephyr/drivers/crc.h 实现:

static int crc_operation(const struct device *const dev, struct crc_ctx *ctx, const uint8_t *src,
			 size_t len)
{
	int ret;

	if (!device_is_ready(crc_dev)) {
		return -ENODEV;
	}

	ret = crc_begin(crc_dev, ctx);
	if (ret != 0) {
		return ret;
	}

	ret = crc_update(crc_dev, ctx, src, len);
	if (ret != 0) {
		return ret;
	}

	ret = crc_finish(crc_dev, ctx);
	if (ret != 0) {
		return ret;
	}

	return 0;
}

驱动接口向下就是 Zephyr 中各支持 CRC 的芯片驱动实现,按照标准的 Zephyr 驱动架构实现并注册以下内容:

__subsystem struct crc_driver_api {
	crc_api_begin begin;
	crc_api_update update;
	crc_api_finish finish;
};

支持的硬件平台 链接到标题

实际的 crc 驱动实现在 zephyr/drivers/crc,目前 Zephyr 只实现了这 4 类芯片的硬件 crc:

zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_NXP crc_nxp.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_NXP_LPC crc_nxp_lpc.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_RENESAS_RA crc_renesas_ra.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_SF32LB crc_sf32lb.c)

这些选项不用去做 Kconfig 配置,在 Devicetree 配置后就会自动选中。

硬件加速生效机制 链接到标题

在 Zephyr 中无需直接通过 Kconfig 配置启用对应的硬件驱动,在 Devicetree 中配置 zephyr,crc 属性,就会启用硬件加速。以思澈 SF32LB 为例说明其如何通过 Devicetree 让硬件加速生效。

zephyr/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.dts 中有如下片段,说明启用了硬件 CRC:

/ {
	chosen {
		zephyr,crc = &crc;
	};
}

&crc {
	status = "okay";
};

zephyr/subsys/crc/Kconfig 中发现有 DT_CHOSEN_Z_CRC 选项存在(由 zephyr,crc 生成)就会选中 CRC_HW_HANDLERCRC_DRIVER

config CRC_HW_HANDLER
	bool "CRC Hardware Accelerator"
	default y if $(dt_chosen_enabled,$(DT_CHOSEN_Z_CRC))
	select CRC_DRIVER
	help
	  Enable use of CRC hardware

zephyr/subsys/crc/CMakeLists.txt 中根据 CONFIG_CRC_HW_HANDLER 将 CRC 硬件驱动 crc_hardware.c 添加到构建中

zephyr_library_sources_ifdef(CONFIG_CRC_HW_HANDLER crc_hardware.c)

zephyr/drivers/crc/Kconfig 中会判断 CRC_DRIVER 存在,就会将支持的硬件 CRC 驱动 Kconfig 加入

source "drivers/crc/Kconfig.nxp"
source "drivers/crc/Kconfig.renesas_ra"
source "drivers/crc/Kconfig.sf32lb"

zephyr/boards/sifli/sf32lb52_devkit_lcd/sf32lb52_devkit_lcd.dts 中,crc 被 enable,脚本处理 DeviceTree 时就会生成 DT_HAS_SIFLI_SF32LB_CRC_ENABLED Kconfig 选项,zephyr/drivers/crc/Kconfig.sf32lb 中会根据这个选项来启用 CRC_DRIVER_SF32LB 和相关支持的硬件 CRC Kconfig 选项。

config CRC_DRIVER_SF32LB
	bool "SiFli SF32LB CRC driver"
	depends on DT_HAS_SIFLI_SF32LB_CRC_ENABLED
	default y
	select CRC_DRIVER_HAS_CRC8
	select CRC_DRIVER_HAS_CRC8_ROHC
	select CRC_DRIVER_HAS_CRC8_CCITT
	select CRC_DRIVER_HAS_CRC16
	select CRC_DRIVER_HAS_CRC16_REFLECT
	select CRC_DRIVER_HAS_CRC16_ANSI
	select CRC_DRIVER_HAS_CRC16_CCITT
	select CRC_DRIVER_HAS_CRC16_ITU_T
	select CRC_DRIVER_HAS_CRC32_IEEE
	select CRC_DRIVER_HAS_CRC32_C
	select CRC_DRIVER_HAS_CRC32_K_4_2
	help

zephyr/drivers/crc/CMakeLists.txt 中根据 CONFIG_CRC_DRIVER_SF32LB 将 CRC 驱动 crc_sf32lb.c 添加到构建中

zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_SF32LB crc_sf32lb.c)

zephyr/subsys/crc/Kconfig 中会根据 zephyr/drivers/crc/Kconfig.sf32lb 中定义的支持的硬件 CRC 算法,选中编译对应 Kconfig 选项,zephyr/subsys/crc/crc_hardware.c 中会根据这些选项,选中编译对应的硬件实现函数,来实现对软件实现弱符号的覆盖,例如 CRC_DRIVER_HAS_CRC8 会选中 CRC8

config CRC8
	bool "CRC-8 (Generic)"
	depends on CRC_DRIVER_HAS_CRC8
	default y
	help
	  Implements a generic CRC-8 algorithm. Useful for small data integrity
	  checks such as checksums and simple communication protocols.

zephyr/subsys/crc/crc_hardware.c 中会根据 CONFIG_CRC8``的实现函数crc8,来实现对软件实现弱符号crc8`的覆盖。

#ifdef CONFIG_CRC8
uint8_t crc8(const uint8_t *src, size_t len, uint8_t polynomial, uint8_t initial_value,
	     bool reversed)
{
	uint8_t flag_reversed;
	int ret;

	if (reversed) {
		flag_reversed = CRC_FLAG_REVERSE_OUTPUT | CRC_FLAG_REVERSE_INPUT;
	}

	struct crc_ctx ctx = {
		.type = CRC8,
		.polynomial = polynomial,
		.seed = initial_value,
		.reversed = flag_reversed,
	};

	ret = crc_operation(crc_dev, &ctx, src, len);
	if (ret != 0) {
		__ASSERT_MSG_INFO("CRC operation failed: %d", ret);
		return 0;
	}
	return ctx.result;
}
#endif

而其它没有被覆盖的CRC算法依然使用软件实现。

参考 链接到标题