From 316c3b4825689afca170576e5a5681302f64fdfa Mon Sep 17 00:00:00 2001 From: Michael Spradling Date: Mon, 29 Jun 2015 21:54:13 -0400 Subject: Add CRC Driver This patch includes a high level and two low level drivers. The high level driver is enabled with flag HAL_USE_CRC The low level drivers include: * Hardware CRC for the STM32 cortex processor lines.(when supported) * Enabled with flag STM32_CRC_USE_CRC1 * DMA is enabled with CRC_USE_DMA * SYNC api will use DMA, but put calling thread to sleep * ASYNC api enabled. * DMA Disabled * SYNC api spin while calculating CRC * ASYNC api disabled * Software CRC (3 modes) * CRCSW_CRC32_TABLE - Enables crc32 with lookup table. * CRCSW_CRC16_TABLE - Enables crc16 with lookup tables. * CRCSW_PROGRAMMBLE - Enables any crc done with computation. * Can calculate any crc configuration. * CRC_USE_DMA obviously not support with software CRC --- os/hal/ports/STM32/LLD/CRCv1/crc_lld.c | 328 +++++++++++++++++++++++++++++++ os/hal/ports/STM32/LLD/CRCv1/crc_lld.h | 249 +++++++++++++++++++++++ os/hal/ports/STM32/STM32F0xx/platform.mk | 6 + 3 files changed, 583 insertions(+) create mode 100644 os/hal/ports/STM32/LLD/CRCv1/crc_lld.c create mode 100644 os/hal/ports/STM32/LLD/CRCv1/crc_lld.h create mode 100644 os/hal/ports/STM32/STM32F0xx/platform.mk (limited to 'os/hal/ports') diff --git a/os/hal/ports/STM32/LLD/CRCv1/crc_lld.c b/os/hal/ports/STM32/LLD/CRCv1/crc_lld.c new file mode 100644 index 0000000..4bcb771 --- /dev/null +++ b/os/hal/ports/STM32/LLD/CRCv1/crc_lld.c @@ -0,0 +1,328 @@ +/* + ChibiOS - Copyright (C) 2015 Michael D. Spradling + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/CRCv1/crc_lld.c + * @brief STM32 CRC subsystem low level driver source. + * + * @addtogroup CRC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_CRC || defined(__DOXYGEN__) + +/** + * Allow CRC Software override for ST drivers. Some ST CRC implimentations + * have limited capabilities. + */ +#if CRCSW_USE_CRC1 != TRUE + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/** + * @brief CRC default configuration. + */ +static const CRCConfig default_config = { + .poly_size = 32, + .poly = 0x04C11DB7, + .initial_val = 0xFFFFFFFF, + .final_val = 0xFFFFFFFF, + .reflect_data = 1, + .reflect_remainder = 1 +}; + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief CRC1 driver identifier.*/ +#if STM32_CRC_USE_CRC1 || defined(__DOXYGEN__) +CRCDriver CRCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +void _crc_lld_calc_byte(CRCDriver *crcp, uint8_t data) { + __IO uint8_t *crc8 = (__IO uint8_t*)&(crcp->crc->DR); + *crc8 = data; +} + +/* + * @brief Returns calculated CRC from last reset + * + * @param[in] crcp pointer to the @p CRCDriver object + * @param[in] data data to be added to crc + * + * @notapi + */ +void _crc_lld_calc_halfword(CRCDriver *crcp, uint16_t data) { + __IO uint16_t *crc16 = (__IO uint16_t*)&(crcp->crc->DR); + *crc16 = data; +} + +/* + * @brief Returns calculated CRC from last reset + * + * @param[in] crcp pointer to the @p CRCDriver object + * @param[in] data data to be added to crc + * + * @notapi + */ +void _crc_lld_calc_word(CRCDriver *crcp, uint32_t data) { + crcp->crc->DR = data; +} + + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/** + * @brief Shared end-of-rx service routine. + * + * @param[in] crcp pointer to the @p CRCDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +#if CRC_USE_DMA == TRUE +static void crc_lld_serve_interrupt(CRCDriver *crcp, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_CRC_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_CRC_DMA_ERROR_HOOK(crcp); + } +#else + (void)flags; +#endif + + /* Stop everything.*/ + dmaStreamDisable(crcp->dma); + + /* Portable CRC ISR code defined in the high level driver, note, it is + a macro.*/ + _crc_isr_code(crcp, crcp->crc->DR ^ crcp->config->final_val); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level CRC driver initialization. + * + * @notapi + */ +void crc_lld_init(void) { + crcObjectInit(&CRCD1); + CRCD1.crc = CRC; +#if CRC_USE_DMA == TRUE + CRCD1.dma = STM32_CRC_CRC1_DMA_STREAM; +#endif +} + +/** + * @brief Configures and activates the CRC peripheral. + * + * @param[in] crcp pointer to the @p CRCDriver object + * + * @notapi + */ +void crc_lld_start(CRCDriver *crcp) { + if (crcp->config == NULL) + crcp->config = &default_config; + + rccEnableCRC(FALSE); + +#if STM32_CRC_PROGRAMMABLE == TRUE + crcp->crc->INIT = crcp->config->initial_val; + crcp->crc->POL = crcp->config->poly; + + crcp->crc->CR = 0; + switch(crcp->config->poly_size) { + case 32: + break; + case 16: + crcp->crc->CR |= CRC_CR_POLYSIZE_0; + break; + case 8: + crcp->crc->CR |= CRC_CR_POLYSIZE_1; + break; + case 7: + crcp->crc->CR |= CRC_CR_POLYSIZE_1 | CRC_CR_POLYSIZE_0; + break; + default: + osalDbgAssert(false, "hardware doesn't support polynomial size"); + break; + }; + if (crcp->config->reflect_data) { + crcp->crc->CR |= CRC_CR_REV_IN_1 | CRC_CR_REV_IN_0; + } + if (crcp->config->reflect_remainder) { + crcp->crc->CR |= CRC_CR_REV_OUT; + } +#else + osalDbgAssert(crcp->config->initial_val != default_config.initial_val, + "hardware doesn't support programmable initial value"); + osalDbgAssert(crcp->config->poly_size != default_config.poly_size, + "hardware doesn't support programmable polynomial size"); + osalDbgAssert(crcp->config->poly != default_config.poly, + "hardware doesn't support programmable polynomial"); + osalDbgAssert(crcp->config->reflect_data != default_config.reflect_data, + "hardware doesn't support reflect of input data"); + osalDbgAssert(crcp->config->reflect_remainder != default_config.reflect_remainder, + "hardware doesn't support reflect of output remainder"); +#endif + +#if CRC_USE_DMA == TRUE +#if STM32_CRC_PROGRAMMABLE == TRUE + crcp->dmamode = STM32_DMA_CR_DIR_M2M | STM32_DMA_CR_PINC | + STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_PSIZE_BYTE | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_PL(STM32_CRC_CRC1_DMA_PRIORITY); +#else + crcp->dmamode = STM32_DMA_CR_DIR_M2M | STM32_DMA_CR_PINC | + STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_PSIZE_WORD | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_PL(STM32_CRC_CRC1_DMA_PRIORITY); +#endif + { + bool b; + b = dmaStreamAllocate(crcp->dma, + STM32_CRC_CRC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)crc_lld_serve_interrupt, + (void *)crcp); + osalDbgAssert(!b, "stream already allocated"); + } +#endif +} + + +/** + * @brief Deactivates the CRC peripheral. + * + * @param[in] crcp pointer to the @p CRCDriver object + * + * @notapi + */ +void crc_lld_stop(CRCDriver *crcp) { +#if CRC_USE_DMA == TRUE + dmaStreamRelease(crcp->dma); +#else + (void)crcp; +#endif + rccDisableCRC(FALSE); +} + +/** + * @brief Resets current CRC calculation. + * + * @param[in] crcp pointer to the @p CRCDriver object + * + * @notapi + */ +void crc_lld_reset(CRCDriver *crcp) { + crcp->crc->CR |= CRC_CR_RESET; +} + +/** + * @brief Returns calculated CRC from last reset + * + * @param[in] crcp pointer to the @p CRCDriver object + * @param[in] n size of buf in bytes + * @param[in] buf @p buffer location + * + * @notapi + */ +uint32_t crc_lld_calc(CRCDriver *crcp, size_t n, const void *buf) { +#if CRC_USE_DMA == TRUE + crc_lld_start_calc(crcp, n, buf); + (void) osalThreadSuspendS(&crcp->thread); +#else + /** + * BUG: Only peform byte writes to DR reg if reflect_data is disabled. + * The STM32 hardware unit seems to incorrectly calculate CRCs when all + * of the following is true: reflect_data(rev_in) is 0, dma is disable, and + * you are writing more than a byte into the DR register. + */ + if (crcp->config->reflect_data != 0) { + while(n > 3) { + _crc_lld_calc_word(crcp, *(uint32_t*)buf); + buf+=4; + n-=4; + } + } + +#if STM32_CRC_PROGRAMMABLE == TRUE + /* Programmable CRC units allow variable register width accesses.*/ + + /** + * BUG: Only peform byte writes to DR reg if reflect_data is disabled. + * The STM32 hardware unit seems to incorrectly calculate CRCs when all + * of the following is true: reflect_data(rev_in) is 0, dma is disable, and + * you are writing more than a byte into the DR register. + */ + if (crcp->config->reflect_data != 0) { + while(n > 1) { + _crc_lld_calc_halfword(crcp, *(uint16_t*)buf); + buf+=2; + n-=2; + } + } + + while(n > 0) { + _crc_lld_calc_byte(crcp, *(uint8_t*)buf); + buf++; + n--; + } +#else + osalDbgAssert(n != 0, "STM32 CRC Unit only supports WORD accesses"); +#endif + +#endif + return crcp->crc->DR ^ crcp->config->final_val; +} + +#if CRC_USE_DMA == TRUE +void crc_lld_start_calc(CRCDriver *crcp, size_t n, const void *buf) { + dmaStreamSetPeripheral(crcp->dma, buf); + dmaStreamSetMemory0(crcp->dma, &crcp->crc->DR); +#if STM32_CRC_PROGRAMMABLE == TRUE + dmaStreamSetTransactionSize(crcp->dma, n); +#else + dmaStreamSetTransactionSize(crcp->dma, (n / 4)); +#endif + dmaStreamSetMode(crcp->dma, crcp->dmamode); + + dmaStreamEnable(crcp->dma); +} +#endif + +#endif /* CRCSW_USE_CRC1 */ + +#endif /* HAL_USE_CRC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/CRCv1/crc_lld.h b/os/hal/ports/STM32/LLD/CRCv1/crc_lld.h new file mode 100644 index 0000000..6f70b42 --- /dev/null +++ b/os/hal/ports/STM32/LLD/CRCv1/crc_lld.h @@ -0,0 +1,249 @@ +/* + ChibiOS - Copyright (C) 2015 Michael D. Spradling + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/CRCv1/crc_lld.h + * @brief STM32 CRC subsystem low level driver header. + * + * @addtogroup CRC + * @{ + */ + +#ifndef _CRC_LLD_H_ +#define _CRC_LLD_H_ + +#if HAL_USE_CRC || defined(__DOXYGEN__) + +/* + * This error check must occur outsite of CRCSW_USE_CRC1 to check if + * two LLD drivers are enabled at the same time + */ +#if STM32_CRC_USE_CRC1 == TRUE && \ + CRCSW_USE_CRC1 == TRUE +#error "Software CRC can't be enable with STM32_CRC_USE_CRC1" +#endif + +/** + * Allow CRC Software override for ST drivers. Some ST CRC implimentations + * have limited capabilities. + */ +#if CRCSW_USE_CRC1 != TRUE + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief CRC1 driver enable switch. + * @details If set to @p TRUE the support for CRC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_CRC_USE_CRC1) || defined(__DOXYGEN__) +#define STM32_CRC_USE_CRC1 FALSE +#endif + +/** + * @brief CRC1 DMA priority (0..3|lowest..highest). + * @note The priority level is for CRC DMA stream. + */ +#if !defined(STM32_CRC_CRC1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_CRC_CRC1_DMA_PRIORITY 2 +#endif + +/** + * @brief CRC1 DMA interrupt priority level setting. + */ +#if !defined(STM32_CRC_CRC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_CRC_CRC1_DMA_IRQ_PRIORITY 1 +#endif + +/** + * @brief CRC1 DMA STREAM to use when performing CRC calculation. + */ +#if !defined(STM32_CRC_CRC1_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_CRC_CRC1_DMA_STREAM STM32_DMA1_STREAM2 +#endif + +/** + * @brief CRC DMA error hook. + */ +#if !defined(STM32_CRC_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_CRC_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_CRC_USE_CRC1 && !STM32_HAS_CRC +#error "Hardware CRC not present in the selected device" +#error "Use CRCSW_USE_CRC1 for software implementation" +#endif + +#if CRC_USE_DMA +#if STM32_CRC_USE_CRC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_CRC_CRC1_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to CRC1" +#endif + +#if STM32_CRC_USE_CRC1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_CRC_CRC1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to CRC1" +#endif + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an CRC driver. + */ +typedef struct CRCDriver CRCDriver; + +/** + * @brief CRC notification callback type + * + * @param[in] crcp pointer to the @ CRCDriver object triggering the + * callback + */ +typedef void (*crccallback_t)(CRCDriver *crcp, uint32_t crc); + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief The size of polynomial to be used for CRC. + */ + uint32_t poly_size; + /** + * @brief The coefficients of the polynomial to be used for CRC. + */ + uint32_t poly; + /** + * @brief The inital value + */ + uint32_t initial_val; + /** + * @brief The final XOR value + */ + uint32_t final_val; + /** + * @brief Reflect bit order data going into CRC + */ + bool reflect_data; + /** + * @brief Reflect bit order of final remainder + */ + bool reflect_remainder; + /* End of the mandatory fields.*/ + /** + * @brief Operation complete callback or @p NULL + */ + crccallback_t end_cb; +} CRCConfig; + + +/** + * @brief Structure representing an CRC driver. + */ +struct CRCDriver { + /** + * @brief Driver state. + */ + crcstate_t state; + /** + * @brief Current configuration data. + */ + const CRCConfig *config; +#if CRC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* CRC_USE_MUTUAL_EXCLUSION */ +#if defined(CRC_DRIVER_EXT_FIELDS) + CRC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the CRCx registers block. + */ + CRC_TypeDef *crc; + +#if CRC_USE_DMA == TRUE + /** + * @brief Waiting thread. + */ + thread_reference_t thread; + /** + * @brief CRC DMA stream + */ + const stm32_dma_stream_t *dma; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; +#endif +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_CRC_USE_CRC1 && !defined(__DOXYGEN__) +extern CRCDriver CRCD1; +#endif /* STM32_CRC_USE_CRC1 */ + +#ifdef __cplusplus +extern "C" { +#endif + void crc_lld_init(void); + void crc_lld_start(CRCDriver *crcp); + void crc_lld_stop(CRCDriver *crcp); + void crc_lld_reset(CRCDriver *crcp); + uint32_t crc_lld_calc(CRCDriver *crcp, size_t n, const void *buf); +#if CRC_USE_DMA + void crc_lld_start_calc(CRCDriver *crcp, size_t n, const void *buf); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* CRCSW_USE_CRC1 */ + +#endif /* HAL_USE_CRC */ + +#endif /* _CRC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/STM32F0xx/platform.mk b/os/hal/ports/STM32/STM32F0xx/platform.mk new file mode 100644 index 0000000..c1c0650 --- /dev/null +++ b/os/hal/ports/STM32/STM32F0xx/platform.mk @@ -0,0 +1,6 @@ +include ${CHIBIOS}/os/hal/ports/STM32/STM32F0xx/platform.mk + +PLATFORMSRC += ${CHIBIOS}/community/os/hal/ports/STM32/LLD/CRCv1/crc_lld.c + +PLATFORMINC += ${CHIBIOS}/community/os/hal/ports/STM32/LLD/CRCv1 \ + ${CHIBIOS}/community/os/hal/ports/STM32/LLD -- cgit v1.2.3