Add thread safety to CRC class

Thread safety is added to serialize the hardware CRC and will not
impact the software CRC.
pull/7781/head
deepikabhavnani 2018-08-13 14:54:56 -05:00 committed by Deepika
parent ec4c33ca80
commit 986411ccb0
3 changed files with 107 additions and 24 deletions

View File

@ -125,11 +125,41 @@ void test_any_polynomial()
}
}
void test_thread(void)
{
char test[] = "123456789";
uint32_t crc;
MbedCRC<POLY_32BIT_ANSI, 32> ct;
TEST_ASSERT_EQUAL(0, ct.compute((void *)test, strlen((const char*)test), &crc));
TEST_ASSERT_EQUAL(0xCBF43926, crc);
}
void test_thread_safety()
{
char test[] = "123456789";
uint32_t crc;
MbedCRC<POLY_16BIT_IBM, 16> ct;
TEST_ASSERT_EQUAL(0, ct.compute_partial_start(&crc));
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test, 4, &crc));
Thread t1(osPriorityNormal1, 320);
t1.start(callback(test_thread));
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test[4], 5, &crc));
TEST_ASSERT_EQUAL(0, ct.compute_partial_stop(&crc));
TEST_ASSERT_EQUAL(0xBB3D, crc);
// Wait for the thread to finish
t1.join();
}
Case cases[] = {
Case("Test supported polynomials", test_supported_polynomials),
Case("Test partial CRC", test_partial_crc),
Case("Test SD CRC polynomials", test_sd_crc),
Case("Test not supported polynomials", test_any_polynomial)
Case("Test not supported polynomials", test_any_polynomial),
Case("Test thread safety", test_thread_safety)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)

View File

@ -22,6 +22,8 @@ namespace mbed {
/** \addtogroup drivers */
/** @{*/
SingletonPtr<PlatformMutex> mbed_crc_mutex;
/* Default values for different types of polynomials
*/
template<>

View File

@ -19,6 +19,8 @@
#include "drivers/TableCRC.h"
#include "hal/crc_api.h"
#include "platform/mbed_assert.h"
#include "platform/SingletonPtr.h"
#include "platform/PlatformMutex.h"
/* This is invalid warning from the compiler for below section of code
if ((width < 8) && (NULL == _crc_table)) {
@ -45,6 +47,7 @@ namespace mbed {
* ROM polynomial tables for supported polynomials (:: crc_polynomial_t) will be used for
* software CRC computation, if ROM tables are not available then CRC is computed runtime
* bit by bit for all data input.
* @note Synchronization level: Thread safe
*
* @tparam polynomial CRC polynomial value in hex
* @tparam width CRC polynomial width
@ -79,12 +82,10 @@ namespace mbed {
* uint32_t crc = 0;
*
* printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
*
* ct.compute_partial_start(&crc);
* ct.compute_partial((void *)&test, 4, &crc);
* ct.compute_partial((void *)&test[4], 5, &crc);
* ct.compute_partial_stop(&crc);
*
* printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
* return 0;
* }
@ -92,8 +93,11 @@ namespace mbed {
* @ingroup drivers
*/
extern SingletonPtr<PlatformMutex> mbed_crc_mutex;
template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32>
class MbedCRC {
public:
enum CrcMode
{
@ -104,7 +108,6 @@ public:
BITWISE
};
public:
typedef uint64_t crc_data_size_t;
/** Lifetime of CRC object
@ -113,7 +116,7 @@ public:
* @param final_xor Final Xor value
* @param reflect_data
* @param reflect_remainder
* @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
* @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
* MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
* MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
* MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
@ -135,6 +138,8 @@ public:
}
/** Compute CRC for the data input
* Compute CRC performs the initialization, computation and collection of
* final CRC.
*
* @param buffer Data bytes
* @param size Size of data
@ -144,52 +149,73 @@ public:
int32_t compute(void *buffer, crc_data_size_t size, uint32_t *crc)
{
MBED_ASSERT(crc != NULL);
int32_t status;
if (0 != (status = compute_partial_start(crc))) {
*crc = 0;
int32_t status = 0;
status = compute_partial_start(crc);
if (0 != status) {
unlock();
return status;
}
if (0 != (status = compute_partial(buffer, size, crc))) {
*crc = 0;
status = compute_partial(buffer, size, crc);
if (0 != status) {
unlock();
return status;
}
if (0 != (status = compute_partial_stop(crc))) {
*crc = 0;
return status;
status = compute_partial_stop(crc);
if (0 != status) {
*crc = 0;
}
return 0;
return status;
}
/** Compute partial CRC for the data input.
*
* CRC data if not available fully, CRC can be computed in parts with available data.
* Previous CRC output should be passed as argument to the current compute_partial call.
* @pre: Call \ref compute_partial_start to start the partial CRC calculation.
* @post: Call \ref compute_partial_stop to get the final CRC value.
*
* In case of hardware, intermediate values and states are saved by hardware and mutex
* locking is used to serialize access to hardware CRC.
*
* In case of software CRC, previous CRC output should be passed as argument to the
* current compute_partial call. Please note the intermediate CRC value is maintained by
* application and not the driver.
*
* @pre: Call `compute_partial_start` to start the partial CRC calculation.
* @post: Call `compute_partial_stop` to get the final CRC value.
*
* @param buffer Data bytes
* @param size Size of data
* @param crc CRC value is intermediate CRC value filled by API.
* @return 0 on success or a negative error code on failure
* @note: CRC as output in compute_partial is not final CRC value, call @ref compute_partial_stop
* @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
* to get final correct CRC value.
*/
int32_t compute_partial(void *buffer, crc_data_size_t size, uint32_t *crc)
{
int32_t status = 0;
switch (_mode) {
#ifdef DEVICE_CRC
case HARDWARE:
hal_crc_compute_partial((uint8_t *)buffer, size);
*crc = 0;
return 0;
break;
#endif
case TABLE:
return table_compute_partial(buffer, size, crc);
status = table_compute_partial(buffer, size, crc);
break;
case BITWISE:
return bitwise_compute_partial(buffer, size, crc);
status = bitwise_compute_partial(buffer, size, crc);
break;
default:
status = -1;
break;
}
return -1;
return status;
}
/** Compute partial start, indicate start of partial computation
@ -200,7 +226,7 @@ public:
* @param crc Initial CRC value set by the API
* @return 0 on success or a negative in case of failure
* @note: CRC is an out parameter and must be reused with compute_partial
* and compute_partial_stop without any modifications in application.
* and `compute_partial_stop` without any modifications in application.
*/
int32_t compute_partial_start(uint32_t *crc)
{
@ -208,6 +234,7 @@ public:
#ifdef DEVICE_CRC
if (_mode == HARDWARE) {
lock();
crc_mbed_config_t config;
config.polynomial = polynomial;
config.width = width;
@ -218,7 +245,7 @@ public:
hal_crc_compute_partial_start(&config);
}
#endif // DEVICE_CRC
#endif
*crc = _initial_value;
return 0;
@ -239,6 +266,7 @@ public:
#ifdef DEVICE_CRC
if (_mode == HARDWARE) {
*crc = hal_crc_get_result();
unlock();
return 0;
}
#endif
@ -252,6 +280,7 @@ public:
} else {
*crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask();
}
unlock();
return 0;
}
@ -281,6 +310,28 @@ private:
uint32_t *_crc_table;
CrcMode _mode;
/** Acquire exclusive access to CRC hardware/software
*/
void lock()
{
#ifdef DEVICE_CRC
if (_mode == HARDWARE) {
mbed_crc_mutex->lock();
}
#endif
}
/** Release exclusive access to CRC hardware/software
*/
virtual void unlock()
{
#ifdef DEVICE_CRC
if (_mode == HARDWARE) {
mbed_crc_mutex->unlock();
}
#endif
}
/** Get the current CRC data size
*
* @return CRC data size in bytes