From 4b51773677e8159c5f603462068d86637d3c066d Mon Sep 17 00:00:00 2001 From: Zoe Kaute Date: Tue, 25 Jun 2024 14:52:18 +0100 Subject: [PATCH 1/4] drivers: crc: Add hardware-based CRC driver Adds support for hardware-based CRC-8-CCITT and CRC-32-IEEE calculations. Signed-off-by: Zoe Kaute --- MAINTAINERS.yml | 16 +++ doc/hardware/peripherals/crc.rst | 21 +++ doc/hardware/peripherals/index.rst | 1 + drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/crc/CMakeLists.txt | 5 + drivers/crc/Kconfig | 21 +++ drivers/crc/Kconfig.stm32 | 9 ++ drivers/crc/crc_stm32.c | 222 +++++++++++++++++++++++++++++ dts/bindings/crc/st,stm32-crc.yaml | 17 +++ include/zephyr/drivers/crc.h | 176 +++++++++++++++++++++++ 11 files changed, 490 insertions(+) create mode 100644 doc/hardware/peripherals/crc.rst create mode 100644 drivers/crc/CMakeLists.txt create mode 100644 drivers/crc/Kconfig create mode 100644 drivers/crc/Kconfig.stm32 create mode 100644 drivers/crc/crc_stm32.c create mode 100644 dts/bindings/crc/st,stm32-crc.yaml create mode 100644 include/zephyr/drivers/crc.h diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index f80a6c302c2d..c52a7337eb17 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -1100,6 +1100,22 @@ Release Notes: tests: - drivers.counter +"Drivers: CRC": + status: maintained + maintainers: + - zkat2 + files: + - drivers/crc/ + - dts/bindings/crc/ + - include/zephyr/drivers/crc.h + - samples/drivers/crc/ + - tests/drivers/crc/ + - doc/hardware/peripherals/crc.rst + labels: + - "area: CRC" + tests: + - drivers.crc + "Drivers: Crypto": status: maintained maintainers: diff --git a/doc/hardware/peripherals/crc.rst b/doc/hardware/peripherals/crc.rst new file mode 100644 index 000000000000..7b1a2cd843f9 --- /dev/null +++ b/doc/hardware/peripherals/crc.rst @@ -0,0 +1,21 @@ +.. _crc_api: + +Cyclic Redundancy Check (CRC) +############################# + +Overview +******** +The Cyclic Redundancy Check (CRC) API provides functions for configuring and computing CRC values +on hardware. + +Configuration Options +********************* + +Related configuration options: + +* :kconfig:option:`CRC_INIT_PRIORITY` + +API Reference +************* + +.. doxygengroup:: crc_interface diff --git a/doc/hardware/peripherals/index.rst b/doc/hardware/peripherals/index.rst index 0af23d02fca0..e0dd1ceb0d5b 100644 --- a/doc/hardware/peripherals/index.rst +++ b/doc/hardware/peripherals/index.rst @@ -20,6 +20,7 @@ Peripherals charger.rst coredump.rst counter.rst + crc.rst dac.rst dma.rst display/index.rst diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index e650898ad638..954779c2d5cd 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control) add_subdirectory_ifdef(CONFIG_CONSOLE console) add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump) add_subdirectory_ifdef(CONFIG_COUNTER counter) +add_subdirectory_ifdef(CONFIG_CRC_HW crc) add_subdirectory_ifdef(CONFIG_CRYPTO crypto) add_subdirectory_ifdef(CONFIG_DAC dac) add_subdirectory_ifdef(CONFIG_DAI dai) diff --git a/drivers/Kconfig b/drivers/Kconfig index 631a0156ec57..170d2ac0cd84 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -18,6 +18,7 @@ source "drivers/clock_control/Kconfig" source "drivers/console/Kconfig" source "drivers/coredump/Kconfig" source "drivers/counter/Kconfig" +source "drivers/crc/Kconfig" source "drivers/crypto/Kconfig" source "drivers/dac/Kconfig" source "drivers/dai/Kconfig" diff --git a/drivers/crc/CMakeLists.txt b/drivers/crc/CMakeLists.txt new file mode 100644 index 000000000000..46b22150b3fd --- /dev/null +++ b/drivers/crc/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2024 Brill Power Ltd. +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_CRC_STM32 crc_stm32.c) diff --git a/drivers/crc/Kconfig b/drivers/crc/Kconfig new file mode 100644 index 000000000000..f7d52880a5dc --- /dev/null +++ b/drivers/crc/Kconfig @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Brill Power Ltd. +# SPDX-License-Identifier: Apache-2.0 + +menuconfig CRC_HW + bool "CRC HW acceleration" + +if CRC_HW + +module = CRC_HW +module-str = CRC_HW +source "subsys/logging/Kconfig.template.log_config" + +config CRC_INIT_PRIORITY + int "CRC init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + CRC driver device initialization priority. + +source "drivers/crc/Kconfig.stm32" + +endif # CRC_HW diff --git a/drivers/crc/Kconfig.stm32 b/drivers/crc/Kconfig.stm32 new file mode 100644 index 000000000000..579b9c4187fa --- /dev/null +++ b/drivers/crc/Kconfig.stm32 @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Brill Power Ltd. +# SPDX-License-Identifier: Apache-2.0 + +config CRC_STM32 + bool + default y + depends on DT_HAS_ST_STM32_CRC_ENABLED + help + Enable the driver implementation for STM32 microcontrollers diff --git a/drivers/crc/crc_stm32.c b/drivers/crc/crc_stm32.c new file mode 100644 index 000000000000..0f19399e8e43 --- /dev/null +++ b/drivers/crc/crc_stm32.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2024 Brill Power Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(crc_stm32, CONFIG_CRC_HW_LOG_LEVEL); + +#include + +#include +#include +#include +#include + +#include +#include + +#define DT_DRV_COMPAT st_stm32_crc + +struct crc_stm32_cfg { + CRC_TypeDef *base; + struct stm32_pclken pclken; +}; + +struct crc_stm32_data { + struct k_sem sem; +}; + +static void crc_lock(const struct device *dev) +{ + struct crc_stm32_data *data = (struct crc_stm32_data *)dev->data; + + k_sem_take(&data->sem, K_FOREVER); +} + +static void crc_unlock(const struct device *dev) +{ + struct crc_stm32_data *data = (struct crc_stm32_data *)dev->data; + + k_sem_give(&data->sem); +} + +static int crc_stm32_begin(const struct device *dev, struct crc_ctx *ctx) +{ + /* Attempt to take semaphore */ + crc_lock(dev); + + /* Ensure ctx is not currently being updated */ + if (ctx->state == CRC_STATE_IN_PROGRESS) { + crc_unlock(dev); + return -EBUSY; + } + + ctx->state = CRC_STATE_IN_PROGRESS; /* Indicate calculation in progress */ + + const struct crc_stm32_cfg *cfg = dev->config; + + LL_CRC_SetInputDataReverseMode(cfg->base, (ctx->flags & CRC_FLAG_REVERSE_INPUT) + ? LL_CRC_INDATA_REVERSE_WORD + : LL_CRC_INDATA_REVERSE_NONE); + LL_CRC_SetOutputDataReverseMode(cfg->base, (ctx->flags & CRC_FLAG_REVERSE_OUTPUT) + ? LL_CRC_OUTDATA_REVERSE_BIT + : LL_CRC_INDATA_REVERSE_NONE); + LL_CRC_ResetCRCCalculationUnit(cfg->base); + + switch (ctx->type) { + case CRC8_CCITT_HW: { + uint8_t init_val = (uint8_t)(ctx->initial_value & 0xFF); + uint8_t poly = (uint8_t)(ctx->polynomial & 0xFF); + + LL_CRC_SetPolynomialSize(cfg->base, LL_CRC_POLYLENGTH_8B); + LL_CRC_SetPolynomialCoef(cfg->base, poly); + LL_CRC_SetInitialData(cfg->base, init_val); + break; + } + + case CRC32_IEEE_HW: { + uint32_t init_val = (uint32_t)(ctx->initial_value & 0xFFFFFFFF); + uint32_t poly = (uint32_t)(ctx->polynomial & 0xFFFFFFFF); + + LL_CRC_SetPolynomialSize(cfg->base, LL_CRC_POLYLENGTH_32B); + LL_CRC_SetPolynomialCoef(cfg->base, poly); + LL_CRC_SetInitialData(cfg->base, init_val); + break; + } + + default: + ctx->state = CRC_STATE_IDLE; + crc_unlock(dev); + return -ENOTSUP; + } + return 0; +} + +static int crc_stm32_update(const struct device *dev, struct crc_ctx *ctx, const void *buffer, + size_t bufsize) +{ + /* Ensure CRC calculation has been initialized by crc_begin() */ + if (ctx->state == CRC_STATE_IDLE) { + return -EINVAL; + } + + const struct crc_stm32_cfg *cfg = dev->config; + const uint8_t *buf = buffer; + + switch (ctx->type) { + case CRC8_CCITT_HW: { + register uint32_t index = 0; + + for (index = 0; index < bufsize; index++) { + LL_CRC_FeedData8(cfg->base, buf[index]); + } + uint8_t result = LL_CRC_ReadData8(cfg->base); + + ctx->result = (crc_result_t)result; + break; + } + + case CRC32_IEEE_HW: { + register uint32_t crc_data = 0; + register uint32_t index = 0; + + /* Compute the CRC of Data Buffer array*/ + for (index = 0; index < (bufsize / 4); index++) { + crc_data = sys_get_le32(buf + 4 * index); + LL_CRC_FeedData32(cfg->base, crc_data); + } + + /* Last bytes specific handling */ + if ((bufsize & 3) != 0) { + if ((bufsize & 3) == 3) { + LL_CRC_FeedData16(cfg->base, sys_get_le16(buf + 4 * index)); + LL_CRC_FeedData8(cfg->base, buf[4 * index + 2]); + } else if ((bufsize & 3) == 2) { + LL_CRC_FeedData16(cfg->base, sys_get_le16(buf + 4 * index)); + } else { /* ((bufsize & 3) == 1) */ + LL_CRC_FeedData8(cfg->base, buf[4 * index]); + } + } + uint32_t result = (~LL_CRC_ReadData32(cfg->base)); + + ctx->result = (crc_result_t)result; + break; + } + + default: + ctx->state = CRC_STATE_IDLE; + crc_unlock(dev); + return -ENOTSUP; + } + + return 0; +} + +static int crc_stm32_finish(const struct device *dev, struct crc_ctx *ctx) +{ + /* Ensure CRC calculation is in progress */ + if (ctx->state == CRC_STATE_IDLE) { + return -EINVAL; + } + + ctx->state = CRC_STATE_IDLE; /* Indicate calculation done */ + crc_unlock(dev); + + return 0; +} + +/** + * @brief Called by Zephyr when instantiating device during boot + * @param dev Pointer to the device structure for the driver instance. + * @retval 0 On success + * @retval -ENODEV Clock control device is not ready + * @retval -EIO Fail to turn on appropriate clock for CRC instance + */ +static int crc_stm32_init(const struct device *dev) +{ + const struct crc_stm32_cfg *cfg = dev->config; + const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + + if (!device_is_ready(clk)) { + LOG_ERR("CRC: Clock control device not ready"); + return -ENODEV; + } + + if (clock_control_on(clk, (clock_control_subsys_t *)&cfg->pclken) != 0) { + LOG_ERR("CRC: Clock control device could not initialise"); + return -EIO; + } + + struct crc_stm32_data *data = dev->data; + + k_sem_init(&data->sem, 1, 1); + + return 0; +} + +static const struct crc_driver_api crc_stm32_driver_api = { + .crc_begin = crc_stm32_begin, + .crc_update = crc_stm32_update, + .crc_finish = crc_stm32_finish, +}; + +#define STM32_CRC_INIT(index) \ + \ + static const struct crc_stm32_cfg crc_stm32_cfg_##index = { \ + .base = (CRC_TypeDef *)DT_INST_REG_ADDR(index), \ + .pclken = \ + { \ + .enr = DT_INST_CLOCKS_CELL(index, bits), \ + .bus = DT_INST_CLOCKS_CELL(index, bus), \ + }, \ + }; \ + \ + static struct crc_stm32_data crc_stm32_data_##index = {}; \ + \ + DEVICE_DT_INST_DEFINE(index, &crc_stm32_init, NULL, &crc_stm32_data_##index, \ + &crc_stm32_cfg_##index, POST_KERNEL, CONFIG_CRC_INIT_PRIORITY, \ + &crc_stm32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(STM32_CRC_INIT) diff --git a/dts/bindings/crc/st,stm32-crc.yaml b/dts/bindings/crc/st,stm32-crc.yaml new file mode 100644 index 000000000000..81128e3feff0 --- /dev/null +++ b/dts/bindings/crc/st,stm32-crc.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Brill Power Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: ST STM32 family CRC + +compatible: "st,stm32-crc" + +include: [base.yaml] + +properties: + reg: + required: true + + clocks: + type: phandle-array + required: true + description: Clock information diff --git a/include/zephyr/drivers/crc.h b/include/zephyr/drivers/crc.h new file mode 100644 index 000000000000..4c07e333208a --- /dev/null +++ b/include/zephyr/drivers/crc.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024 Brill Power Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_CRC_H +#define ZEPHYR_INCLUDE_DRIVERS_CRC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CRC driver APIs + * @defgroup crc_interface CRC driver APIs + * @ingroup io_interfaces + * @{ + */ + +#define CRC_FLAG_REVERSE_INPUT BIT(0) +#define CRC_FLAG_REVERSE_OUTPUT BIT(1) + +#define CRC4_INIT_VAL 0x0 +#define CRC4_TI_INIT_VAL 0x0 +#define CRC7_BE_INIT_VAL 0x0 +#define CRC8_INIT_VAL 0x0 +#define CRC8_CCITT_INIT_VAL 0x00 +#define CRC16_INIT_VAL 0x0 +#define CRC16_ANSI_INIT_VAL 0x0 +#define CRC16_CCITT_INIT_VAL 0x0000 +#define CRC16_ITU_T_INIT_VAL 0x0000 +#define CRC24_PGP_INIT_VAL 0x0 +#define CRC32_C_INIT_VAL 0x00000000 +#define CRC32_IEEE_INIT_VAL 0xFFFFFFFFU + +#define CRC8_CCITT_POLY 0x07 +#define CRC32_IEEE_POLY 0x04C11DB7U + +/** + * @brief CRC algorithm enumeration + */ +enum crc_type_hw { + CRC4_HW, + CRC4_TI_HW, + CRC7_BE_HW, + CRC8_HW, + CRC8_CCITT_HW, + CRC16_HW, + CRC16_ANSI_HW, + CRC16_CCITT_HW, + CRC16_ITU_T_HW, + CRC24_PGP_HW, + CRC32_C_HW, + CRC32_IEEE_HW, +}; + +/** + * @brief CRC state enumeration + */ +enum crc_state { + CRC_STATE_IDLE, + CRC_STATE_IN_PROGRESS +}; + +typedef uint64_t crc_init_val_t; +typedef uint64_t crc_polynomial_t; +typedef uint64_t crc_result_t; + +/** + * @brief CRC context structure + */ +struct crc_ctx { + enum crc_type_hw type; /**< Type of CRC algorithm */ + enum crc_state state; /**< State of CRC calculation */ + crc_init_val_t initial_value; /**< Initial value for CRC calculation */ + uint32_t flags; /**< Flags for CRC calculation */ + crc_polynomial_t polynomial; /**< Polynomial used in CRC calculation */ + crc_result_t result; /**< Store the CRC result */ +}; + +typedef int (*crc_api_begin)(const struct device *dev, struct crc_ctx *ctx); +typedef int (*crc_api_update)(const struct device *dev, struct crc_ctx *ctx, const void *buffer, + size_t bufsize); +typedef int (*crc_api_finish)(const struct device *dev, struct crc_ctx *ctx); + +/** + * @brief CRC driver API structure + */ +__subsystem struct crc_driver_api { + crc_api_begin crc_begin; + crc_api_update crc_update; + crc_api_finish crc_finish; +}; + +/** + * @brief Configure CRC unit for calculation + * @param dev Pointer to the device structure + * @param ctx Pointer to the CRC context structure + * @retval 0 if successful, negative errno code on failure + */ +__syscall int crc_begin(const struct device *dev, struct crc_ctx *ctx); +static inline int z_impl_crc_begin(const struct device *dev, struct crc_ctx *ctx) +{ + const struct crc_driver_api *api = (const struct crc_driver_api *)dev->api; + + return api->crc_begin(dev, ctx); +} + +/** + * @brief Perform CRC calculation on the provided data buffer and retrieve result + * @param dev Pointer to the device structure + * @param ctx Pointer to the CRC context structure + * @param buffer Pointer to array to be CRCd + * @param bufsize Number of bytes in *buffer + * @retval 0 if successful, negative errno code on failure + */ +__syscall int crc_update(const struct device *dev, struct crc_ctx *ctx, const void *buffer, + size_t bufsize); +static inline int z_impl_crc_update(const struct device *dev, struct crc_ctx *ctx, + const void *buffer, size_t bufsize) +{ + const struct crc_driver_api *api = (const struct crc_driver_api *)dev->api; + + return api->crc_update(dev, ctx, buffer, bufsize); +} + +/** + * @brief Finalize CRC calculation + * @param dev Pointer to the device structure + * @param ctx Pointer to the CRC context structure + * @retval 0 if successful, negative errno code on failure + */ +__syscall int crc_finish(const struct device *dev, struct crc_ctx *ctx); +static inline int z_impl_crc_finish(const struct device *dev, struct crc_ctx *ctx) +{ + const struct crc_driver_api *api = (const struct crc_driver_api *)dev->api; + + return api->crc_finish(dev, ctx); +} + +/** + * @brief Verify CRC calculation + * @param ctx Pointer to the CRC context structure + * @param expected Expected CRC result + * @retval 0 if successful, negative errno code on failure + */ +static inline int crc_verify(struct crc_ctx *ctx, crc_result_t expected) +{ + if (ctx->state == CRC_STATE_IN_PROGRESS) { + return -EBUSY; + } + + crc_result_t res = ctx->result; + + if (res != expected) { + return -EINVAL; + } + + return 0; +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CRC_H */ From d99de4ada14b75a8e3a89b420581f11765e65795 Mon Sep 17 00:00:00 2001 From: Zoe Kaute Date: Fri, 2 Aug 2024 10:30:52 +0100 Subject: [PATCH 2/4] samples: drivers: crc Add sample for hardware based CRC API Introduces a new sample that demonstrates how to use the hardware-based CRC API. Signed-off-by: Zoe Kaute --- samples/drivers/crc/CMakeLists.txt | 8 ++++ samples/drivers/crc/README.rst | 24 +++++++++++ samples/drivers/crc/prj.conf | 3 ++ samples/drivers/crc/sample.yaml | 10 +++++ samples/drivers/crc/src/main.c | 69 ++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 samples/drivers/crc/CMakeLists.txt create mode 100644 samples/drivers/crc/README.rst create mode 100644 samples/drivers/crc/prj.conf create mode 100644 samples/drivers/crc/sample.yaml create mode 100644 samples/drivers/crc/src/main.c diff --git a/samples/drivers/crc/CMakeLists.txt b/samples/drivers/crc/CMakeLists.txt new file mode 100644 index 000000000000..41701099abab --- /dev/null +++ b/samples/drivers/crc/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(crc_sample) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/crc/README.rst b/samples/drivers/crc/README.rst new file mode 100644 index 000000000000..d9b4c9823291 --- /dev/null +++ b/samples/drivers/crc/README.rst @@ -0,0 +1,24 @@ +.. zephyr:code-sample:: crc + :name: Cyclic Redundancy Check (CRC) + :relevant-api: crc_interface + + Compute and verify a CRC checksum using the CRC driver API. + +Overview +******** + +This sample demonstrates how to use the :ref:`CRC driver API `. + +Building and Running +******************** + +Building and Running for ST Nucleo G474RE +========================================= +The sample can be built and executed for the +:ref:`nucleo_g474re_board` as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/crc + :board: nucleo_g474re + :goals: build flash + :compact: diff --git a/samples/drivers/crc/prj.conf b/samples/drivers/crc/prj.conf new file mode 100644 index 000000000000..f2cfe21735d9 --- /dev/null +++ b/samples/drivers/crc/prj.conf @@ -0,0 +1,3 @@ +CONFIG_CRC_HW=y +CONFIG_CRC_HW_LOG_LEVEL_INF=y +CONFIG_LOG=y diff --git a/samples/drivers/crc/sample.yaml b/samples/drivers/crc/sample.yaml new file mode 100644 index 000000000000..400e1890b3cb --- /dev/null +++ b/samples/drivers/crc/sample.yaml @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +sample: + name: CRC Sample +tests: + crc_sample.test: + depends_on: crc + tags: + - drivers + - crc diff --git a/samples/drivers/crc/src/main.c b/samples/drivers/crc/src/main.c new file mode 100644 index 000000000000..662b9755afc8 --- /dev/null +++ b/samples/drivers/crc/src/main.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 Brill Power Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(crc_example, CONFIG_LOG_DEFAULT_LEVEL); + +#include +#include +#include + +int main(void) +{ + /* Retrieve the CRC device */ + static const struct device *dev = DEVICE_DT_GET(DT_ALIAS(crc0)); + + /* Ensure the device is ready */ + if (!device_is_ready(dev)) { + LOG_ERR("CRC device not found or not ready\n"); + return -ENODEV; + } + + /* Define the data to compute CRC */ + uint8_t data[8] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4}; + + /* Define the CRC context */ + struct crc_ctx ctx = {.type = CRC8_CCITT_HW, + .flags = 0, + .polynomial = CRC8_CCITT_POLY, + .initial_value = CRC8_CCITT_INIT_VAL}; + + /* Start CRC computation */ + int ret = crc_begin(dev, &ctx); + + if (ret != 0) { + LOG_ERR("Failed to begin CRC: %d\n", ret); + return ret; + } + + /* Update CRC computation */ + ret = crc_update(dev, &ctx, data, 8); + + if (ret != 0) { + LOG_ERR("Failed to update CRC: %d\n", ret); + return ret; + } + + /* Finish CRC computation */ + ret = crc_finish(dev, &ctx); + + if (ret != 0) { + LOG_ERR("Failed to finish CRC: %d\n", ret); + return ret; + } + + /* Verify the computed CRC (example expected value: 0x4D) */ + ret = crc_verify(&ctx, 0x4D); + + if (ret != 0) { + LOG_ERR("CRC verification failed: %d\n", ret); + return ret; + } + + LOG_INF("CRC verification succeeded"); + + return ret; +} From 7106ea38479d96e2e10154e4d37933f7b23cd561 Mon Sep 17 00:00:00 2001 From: Zoe Kaute Date: Fri, 2 Aug 2024 10:47:42 +0100 Subject: [PATCH 3/4] tests: drivers: crc: Add test suite for hardware-based CRC API Add a test suite to validate the hardware-based CRC API. Signed-off-by: Zoe Kaute --- tests/drivers/crc/CMakeLists.txt | 8 ++ tests/drivers/crc/prj.conf | 4 + tests/drivers/crc/src/main.c | 213 +++++++++++++++++++++++++++++++ tests/drivers/crc/testcase.yaml | 8 ++ 4 files changed, 233 insertions(+) create mode 100644 tests/drivers/crc/CMakeLists.txt create mode 100644 tests/drivers/crc/prj.conf create mode 100644 tests/drivers/crc/src/main.c create mode 100644 tests/drivers/crc/testcase.yaml diff --git a/tests/drivers/crc/CMakeLists.txt b/tests/drivers/crc/CMakeLists.txt new file mode 100644 index 000000000000..2b5f5084a831 --- /dev/null +++ b/tests/drivers/crc/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(crc_test) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/drivers/crc/prj.conf b/tests/drivers/crc/prj.conf new file mode 100644 index 000000000000..05fcfa7fed66 --- /dev/null +++ b/tests/drivers/crc/prj.conf @@ -0,0 +1,4 @@ +CONFIG_ZTEST=y + +CONFIG_CRC_HW=y +CONFIG_CRC_HW_LOG_LEVEL_INF=y diff --git a/tests/drivers/crc/src/main.c b/tests/drivers/crc/src/main.c new file mode 100644 index 000000000000..8379919d3486 --- /dev/null +++ b/tests/drivers/crc/src/main.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2024 Brill Power Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define WAIT_THREAD_STACK_SIZE 1024 +#define WAIT_THREAD_PRIO -10 + +static void wait_thread_entry(void *a, void *b, void *c); + +K_THREAD_STACK_DEFINE(wait_thread_stack_area, WAIT_THREAD_STACK_SIZE); +struct k_thread wait_thread_data; + +/** + * 1) Take the semaphore + * 2) Sleep for 50 ms (to allow ztest main thread to attempt to acquire semaphore) + * 3) Give the semaphore + */ +static void wait_thread_entry(void *a, void *b, void *c) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + + uint8_t data[8] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4}; + + struct crc_ctx ctx = {.type = CRC8_CCITT_HW, + .flags = 0, + .polynomial = CRC8_CCITT_POLY, + .initial_value = CRC8_CCITT_INIT_VAL}; + + crc_begin(dev, &ctx); + + k_sleep(K_MSEC(50)); + + crc_update(dev, &ctx, data, sizeof(data)); + crc_finish(dev, &ctx); + zassert_equal(crc_verify(&ctx, 0x4D), 0); +} + +/** + * @brief Test that crc_8_ccitt works + */ +ZTEST(crc, test_crc_8_ccitt) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + + uint8_t data[8] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4}; + + struct crc_ctx ctx = {.type = CRC8_CCITT_HW, + .flags = 0, + .polynomial = CRC8_CCITT_POLY, + .initial_value = CRC8_CCITT_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data, sizeof(data)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0x4D), 0); +} + +/** + * @brief Test that crc_32_ieee works + */ +ZTEST(crc, test_crc_32_ieee_remain_0) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + + uint8_t data[8] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4}; + + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data, sizeof(data)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0xCEA4A6C2), 0); +} + +/** + * @brief Test that crc_32_ieee works with a single byte + * remaining after the main process loop + */ +ZTEST(crc, test_crc_32_ieee_remain_1) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + + uint8_t data[9] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4, 0x3B}; + + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data, sizeof(data)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0x16AD0193), 0); +} + +/** + * @brief Test that crc_32_ieee works with two bytes + * remaining after the main process loop + */ +ZTEST(crc, test_crc_32_ieee_remain_2) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + + uint8_t data[10] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4, 0x3B, 0x78}; + + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data, sizeof(data)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0xE5CC797C), 0); +} + +/** + * @brief Test that crc_32_ieee works with three bytes + * remaining after the main process loop + */ +ZTEST(crc, test_crc_32_ieee_remain_3) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + + uint8_t data[11] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4, 0x3B, 0x78, 0xB6}; + + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data, sizeof(data)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0xA956085A), 0); +} + +/** + * @brief Test CRC calculation with discontinuous buffers. + */ +ZTEST(crc, test_discontinuous_buf) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + uint8_t data1[5] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E}; + uint8_t data2[5] = {0x49, 0x00, 0xC4, 0x3B, 0x78}; + + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + zassert_equal(crc_begin(dev, &ctx), 0); + zassert_equal(crc_update(dev, &ctx, data1, sizeof(data1)), 0); + zassert_equal(crc_update(dev, &ctx, data2, sizeof(data2)), 0); + zassert_equal(crc_finish(dev, &ctx), 0); + zassert_equal(crc_verify(&ctx, 0xE5CC797C), 0); +} + +/** + * @brief Test CRC function semaphore wait for thread safety + * + * Verifies that CRC operations are blocked until a semaphore is released. A new thread + * acquires the semaphore, and the main thread's CRC operations wait until it is released. + */ +ZTEST(crc, test_crc_threadsafe) +{ + static const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(crc)); + + uint8_t data[11] = {0x0A, 0x2B, 0x4C, 0x6D, 0x8E, 0x49, 0x00, 0xC4, 0x3B, 0x78, 0xB6}; + uint32_t flags = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT; + struct crc_ctx ctx = {.type = CRC32_IEEE_HW, + .flags = flags, + .polynomial = CRC32_IEEE_POLY, + .initial_value = CRC32_IEEE_INIT_VAL}; + + /** + * Create new thread that will immediately take the semaphore + */ + k_thread_create(&wait_thread_data, wait_thread_stack_area, + K_THREAD_STACK_SIZEOF(wait_thread_stack_area), wait_thread_entry, NULL, + NULL, NULL, WAIT_THREAD_PRIO, 0, K_NO_WAIT); + + /** + * Sleep for 10 ms to ensure that new thread has taken lock + */ + k_sleep(K_MSEC(10)); + + /** + * Attempt to take semaphore, this should wait for the new thread to give the semaphore + * before executing + */ + crc_begin(dev, &ctx); + crc_update(dev, &ctx, data, sizeof(data)); + crc_finish(dev, &ctx); + zassert_equal(crc_verify(&ctx, 0xA956085A), 0); +} + +ZTEST_SUITE(crc, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/crc/testcase.yaml b/tests/drivers/crc/testcase.yaml new file mode 100644 index 000000000000..48903e951845 --- /dev/null +++ b/tests/drivers/crc/testcase.yaml @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +tests: + drivers.crc: + depends_on: crc + tags: + - drivers + - crc From d75c1219b073ba7eae61223d14ec0047c3cc9e9d Mon Sep 17 00:00:00 2001 From: Zoe Kaute Date: Fri, 2 Aug 2024 10:55:45 +0100 Subject: [PATCH 4/4] dts: arm: st: g4: Add CRC node Add the CRC node to stm32g4 device tree source file. Signed-off-by: Zoe Kaute --- boards/st/nucleo_g474re/nucleo_g474re.dts | 5 +++++ boards/st/nucleo_g474re/nucleo_g474re.yaml | 1 + dts/arm/st/g4/stm32g4.dtsi | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/boards/st/nucleo_g474re/nucleo_g474re.dts b/boards/st/nucleo_g474re/nucleo_g474re.dts index a91ec3ccb603..d28e78c097ba 100644 --- a/boards/st/nucleo_g474re/nucleo_g474re.dts +++ b/boards/st/nucleo_g474re/nucleo_g474re.dts @@ -57,6 +57,7 @@ die-temp0 = &die_temp; volt-sensor0 = &vref; volt-sensor1 = &vbat; + crc0 = &crc; }; }; @@ -73,6 +74,10 @@ status = "okay"; }; +&crc { + status = "okay"; +}; + &pll { div-m = <6>; mul-n = <85>; diff --git a/boards/st/nucleo_g474re/nucleo_g474re.yaml b/boards/st/nucleo_g474re/nucleo_g474re.yaml index 26c4254294ff..46fb2979fc5b 100644 --- a/boards/st/nucleo_g474re/nucleo_g474re.yaml +++ b/boards/st/nucleo_g474re/nucleo_g474re.yaml @@ -25,4 +25,5 @@ supported: - dma - can - rtc + - crc vendor: st diff --git a/dts/arm/st/g4/stm32g4.dtsi b/dts/arm/st/g4/stm32g4.dtsi index 8cba3a580215..dde4c6d0dcca 100644 --- a/dts/arm/st/g4/stm32g4.dtsi +++ b/dts/arm/st/g4/stm32g4.dtsi @@ -133,6 +133,12 @@ st,adc-sequencer = ; }; + crc: crc@40023000 { + compatible = "st,stm32-crc"; + reg = <0x40023000 0xA0>; + clocks = <&rcc STM32_CLOCK_BUS_AHB1 0x1000>; + }; + dac1: dac@50000800 { compatible = "st,stm32-dac"; reg = <0x50000800 0x400>;