dsp: utils: Implemented float/fixed type conversions

The DSP library does not provide functions to convert fixed
representations (Q7,Q15,Q31) to float or double and viceversa. This commit
implements those functions using a shift argument to indicate the number
of decimal places.

Signed-off-by: Matías Román Camilletti <matias.camilletti@owl-services.com>
This commit is contained in:
Matías Román Camilletti 2024-11-25 12:52:38 -03:00 committed by Benjamin Cabé
parent 527d1020fe
commit 4ca5acac45
9 changed files with 488 additions and 0 deletions

175
include/zephyr/dsp/utils.h Normal file
View file

@ -0,0 +1,175 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file zephyr/dsp/utils.h
*
* @brief Extra functions and macros for DSP
*/
#ifndef INCLUDE_ZEPHYR_DSP_UTILS_H_
#define INCLUDE_ZEPHYR_DSP_UTILS_H_
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/dsp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @ingroup math_dsp
* @defgroup math_dsp_utils_shifts Float/Fixed point shift conversion functions
*/
/**
* @ingroup math_dsp_utils_shifts
* @addtogroup math_dsp_basic_conv_to_float Fixed to Float point conversions
*
* Convert number Q7/Q15/Q31 to Float or Double representation with shift.
*
* There are separate functions for floating-point, Q7, Q15, and Q31 data types.
* @{
*/
/**
* @brief Convert a Q7 fixed-point value to a floating-point (float32_t) value with a left shift.
*
* @param src The input Q7 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 7).
* @return The converted floating-point (float32_t) value.
*/
#define Z_SHIFT_Q7_TO_F32(src, m) ((float32_t)(((src << m)) / (float32_t)(1U << 7)))
/**
* @brief Convert a Q15 fixed-point value to a floating-point (float32_t) value with a left shift.
*
* @param src The input Q15 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 15).
* @return The converted floating-point (float32_t) value.
*/
#define Z_SHIFT_Q15_TO_F32(src, m) ((float32_t)((src << m) / (float32_t)(1U << 15)))
/**
* @brief Convert a Q31 fixed-point value to a floating-point (float32_t) value with a left shift.
*
* @param src The input Q31 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 31).
* @return The converted floating-point (float32_t) value.
*/
#define Z_SHIFT_Q31_TO_F32(src, m) ((float32_t)(((int64_t)src) << m) / (float32_t)(1U << 31))
/**
* @brief Convert a Q7 fixed-point value to a floating-point (float64_t) value with a left shift.
*
* @param src The input Q7 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 7).
* @return The converted floating-point (float64_t) value.
*/
#define Z_SHIFT_Q7_TO_F64(src, m) (((float64_t)(src << m)) / (1U << 7))
/**
* @brief Convert a Q15 fixed-point value to a floating-point (float64_t) value with a left shift.
*
* @param src The input Q15 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 15).
* @return The converted floating-point (float64_t) value.
*/
#define Z_SHIFT_Q15_TO_F64(src, m) (((float64_t)(src << m)) / (1UL << 15))
/**
* @brief Convert a Q31 fixed-point value to a floating-point (float64_t) value with a left shift.
*
* @param src The input Q31 fixed-point value.
* @param m The number of bits to left shift the input value (0 to 31).
* @return The converted floating-point (float64_t) value.
*/
#define Z_SHIFT_Q31_TO_F64(src, m) ((float64_t)(((int64_t)src) << m) / (1ULL << 31))
/**
* @}
*/
/**
* @ingroup math_dsp_utils_shifts
* @addtogroup math_dsp_basic_conv_to_fixed Float to Fixed point conversions
*
* Convert number representation in Float or Double to Q31/Q15/Q7.
*
* There are separate functions for floating-point, Q7, Q15, and Q31 data types.
* @{
*/
/**
* @brief Convert a floating-point (float32_t) value to a Q7 fixed-point value with a right shift.
*
* @param src The input floating-point (float32_t) value.
* @param m The number of bits to right shift the input value (0 to 7).
* @return The converted Q7 fixed-point value.
*/
#define Z_SHIFT_F32_TO_Q7(src, m) \
((q7_t)Z_CLAMP((int32_t)(src * (1U << 7)) >> m, INT8_MIN, INT8_MAX))
/**
* @brief Convert a floating-point (float32_t) value to a Q15 fixed-point value with a right shift.
*
* @param src The input floating-point (float32_t) value.
* @param m The number of bits to right shift the input value (0 to 15).
* @return The converted Q15 fixed-point value.
*/
#define Z_SHIFT_F32_TO_Q15(src, m) \
((q15_t)Z_CLAMP((int32_t)(src * (1U << 15)) >> m, INT16_MIN, INT16_MAX))
/**
* @brief Convert a floating-point (float32_t) value to a Q31 fixed-point value with a right shift.
*
* @param src The input floating-point (float32_t) value.
* @param m The number of bits to right shift the input value (0 to 31).
* @return The converted Q31 fixed-point value.
*/
#define Z_SHIFT_F32_TO_Q31(src, m) \
((q31_t)Z_CLAMP((int64_t)(src * (1U << 31)) >> m, INT32_MIN, INT32_MAX))
/**
* @brief Convert a floating-point (float64_t) value to a Q7 fixed-point value with a right shift.
*
* @param src The input floating-point (float64_t) value.
* @param m The number of bits to right shift the input value (0 to 7).
* @return The converted Q7 fixed-point value.
*/
#define Z_SHIFT_F64_TO_Q7(src, m) \
((q7_t)Z_CLAMP((int32_t)(src * (1U << 7)) >> m, INT8_MIN, INT8_MAX))
/**
* @brief Convert a floating-point (float64_t) value to a Q15 fixed-point value with a right shift.
*
* @param src The input floating-point (float64_t) value.
* @param m The number of bits to right shift the input value (0 to 15).
* @return The converted Q15 fixed-point value.
*/
#define Z_SHIFT_F64_TO_Q15(src, m) \
((q15_t)Z_CLAMP((int32_t)(src * (1U << 15)) >> m, INT16_MIN, INT16_MAX))
/**
* @brief Convert a floating-point (float64_t) value to a Q31 fixed-point value with a right shift.
*
* @param src The input floating-point (float64_t) value.
* @param m The number of bits to right shift the input value (0 to 31).
* @return The converted Q31 fixed-point value.
*/
#define Z_SHIFT_F64_TO_Q31(src, m) \
((q31_t)Z_CLAMP((int64_t)(src * (1U << 31)) >> m, INT32_MIN, INT32_MAX))
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* INCLUDE_ZEPHYR_DSP_UTILS_H_ */

View file

@ -0,0 +1,15 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(dsp_conversions)
target_sources(app PRIVATE
src/q7.c
src/q15.c
src/q31.c
src/f32.c
src/f64.c
)
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/tests/lib/cmsis_dsp)

View file

@ -0,0 +1,6 @@
CONFIG_ZTEST=y
CONFIG_DSP=y
CONFIG_DSP_BACKEND_CMSIS=y
CONFIG_CMSIS_DSP=y
CONFIG_CMSIS_DSP_SUPPORT=y
CONFIG_REQUIRES_FLOAT_PRINTF=y

View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/utils.h>
#include "common/test_common.h"
#define DEFINE_MULTIPLE_TEST_CASES(constructor, variant, array) \
FOR_EACH_IDX_FIXED_ARG(constructor, (), variant, array)
#define RE_DEFINE_TEST_VARIANT3(...) DEFINE_TEST_VARIANT3(__VA_ARGS__)
#define DEFINE_SHIFT_F32_CASE(n, args, func) \
RE_DEFINE_TEST_VARIANT3(shift_f32, func, n, __DEBRACKET args)
#define TEST_CASES_SHIFT_F32_TO_Q7 (-1.0F, 0, -128), (1.0F, 0, 127), (1.0F, 7, 1), (-1.0F, 7, -1)
#define TEST_CASES_SHIFT_F32_TO_Q15 \
(-1.0F, 0, -32768), (1.0F, 0, 32767), (1.0F, 15, 1), (-1.0F, 15, -1)
#define TEST_CASES_SHIFT_F32_TO_Q31 \
(-1.0F, 0, -2147483648), (1.0F, 0, 2147483647), (1.0F, 31, 1), (-1.0F, 31, -1)
static void test_shift_f32_to_q7(const float32_t data, const uint32_t shift,
const DSP_DATA q31_t expected)
{
q7_t shifted_data = Z_SHIFT_F32_TO_Q7(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", (double)data, shift,
shifted_data, expected);
}
static void test_shift_f32_to_q15(const float32_t data, const uint32_t shift,
const DSP_DATA q31_t expected)
{
q15_t shifted_data = Z_SHIFT_F32_TO_Q15(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", (double)data, shift,
shifted_data, expected);
}
static void test_shift_f32_to_q31(const float32_t data, const uint32_t shift,
const DSP_DATA q31_t expected)
{
q31_t shifted_data = Z_SHIFT_F32_TO_Q31(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", (double)data, shift,
shifted_data, expected);
}
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F32_CASE, shift_f32_to_q7, TEST_CASES_SHIFT_F32_TO_Q7)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F32_CASE, shift_f32_to_q15, TEST_CASES_SHIFT_F32_TO_Q15)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F32_CASE, shift_f32_to_q31, TEST_CASES_SHIFT_F32_TO_Q31)
ZTEST_SUITE(shift_f32, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/utils.h>
#include "common/test_common.h"
#define DEFINE_MULTIPLE_TEST_CASES(constructor, variant, array) \
FOR_EACH_IDX_FIXED_ARG(constructor, (), variant, array)
#define RE_DEFINE_TEST_VARIANT3(...) DEFINE_TEST_VARIANT3(__VA_ARGS__)
#define DEFINE_SHIFT_F64_CASE(n, args, func) \
RE_DEFINE_TEST_VARIANT3(shift_f64, func, n, __DEBRACKET args)
#define TEST_CASES_SHIFT_F64_TO_Q7 (-1.0, 0, -128), (1.0, 0, 127), (1.0, 7, 1), (-1.0, 7, -1)
#define TEST_CASES_SHIFT_F64_TO_Q15 (-1.0, 0, -32768), (1.0, 0, 32767), (1.0, 15, 1), (-1.0, 15, -1)
#define TEST_CASES_SHIFT_F64_TO_Q31 \
(-1.0, 0, INT32_MIN), (1.0, 0, INT32_MAX), (1.0, 31, 1), (-1.0, 31, -1)
static void test_shift_f64_to_q7(const float64_t data, const uint32_t shift,
const DSP_DATA q7_t expected)
{
const q7_t shifted_data = Z_SHIFT_F64_TO_Q7(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", data, shift,
shifted_data, expected);
}
static void test_shift_f64_to_q15(const float64_t data, const uint32_t shift,
const DSP_DATA q15_t expected)
{
const q15_t shifted_data = Z_SHIFT_F64_TO_Q15(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", (double)data, shift,
shifted_data, expected);
}
static void test_shift_f64_to_q31(const float64_t data, const uint32_t shift,
const DSP_DATA q31_t expected)
{
const q31_t shifted_data = Z_SHIFT_F64_TO_Q31(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: %f shifted by %d = %d (expected %d)", (double)data, shift,
shifted_data, expected);
}
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F64_CASE, shift_f64_to_q7, TEST_CASES_SHIFT_F64_TO_Q7)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F64_CASE, shift_f64_to_q15, TEST_CASES_SHIFT_F64_TO_Q15)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_F64_CASE, shift_f64_to_q31, TEST_CASES_SHIFT_F64_TO_Q31)
ZTEST_SUITE(shift_f64, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/utils.h>
#include "common/test_common.h"
#define DEFINE_MULTIPLE_TEST_CASES(constructor, variant, array) \
FOR_EACH_IDX_FIXED_ARG(constructor, (), variant, array)
#define RE_DEFINE_TEST_VARIANT3(...) DEFINE_TEST_VARIANT3(__VA_ARGS__)
#define DEFINE_SHIFT_Q15_CASE(n, args, func) \
RE_DEFINE_TEST_VARIANT3(shift_q15, func, n, __DEBRACKET args)
#define TEST_CORNER_CASES_SHIFT_Q15_TO_F32 \
(-32768, 0, -1.0F), (32767, 0, 0.999969482421875F), (32767, 15, 32767.0F), \
(-32768, 15, -32768.0F)
#define TEST_CORNER_CASES_SHIFT_Q15_TO_F64 \
(-32768, 0, -1), (32767, 0, 0.999969482421875), (32767, 15, 32767.0), (-32768, 15, -32768.0)
static void test_shift_q15_to_f32(const q15_t data, const uint32_t shift, const float32_t expected)
{
const float32_t shifted_data = Z_SHIFT_Q15_TO_F32(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
(double)shifted_data, (double)expected);
}
static void test_shift_q15_to_f64(const q15_t data, const uint32_t shift, const float64_t expected)
{
const float64_t shifted_data = Z_SHIFT_Q15_TO_F64(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
shifted_data, expected);
}
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q15_CASE, shift_q15_to_f32,
TEST_CORNER_CASES_SHIFT_Q15_TO_F32)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q15_CASE, shift_q15_to_f64,
TEST_CORNER_CASES_SHIFT_Q15_TO_F64)
ZTEST_SUITE(shift_q15, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/utils.h>
#include "common/test_common.h"
#define DEFINE_MULTIPLE_TEST_CASES(constructor, variant, array) \
FOR_EACH_IDX_FIXED_ARG(constructor, (), variant, array)
#define RE_DEFINE_TEST_VARIANT3(...) DEFINE_TEST_VARIANT3(__VA_ARGS__)
#define DEFINE_SHIFT_Q31_CASE(n, args, func) \
RE_DEFINE_TEST_VARIANT3(shift_q31, func, n, __DEBRACKET args)
#define TEST_CORNER_CASES_SHIFT_Q31_TO_F32 \
(2147483647, 0, 1.0F), (-2147483648, 0, -1.0F), (-2147483648, 31, -2147483648.0F), \
(2147483647, 31, 2147483647.0F)
#define TEST_CORNER_CASES_SHIFT_Q31_TO_F64 \
(2147483647, 0, 0.9999999995343387), (-2147483648, 0, -1.0), \
(-2147483648, 31, -2147483648.0), (2147483647, 31, 2147483647.0)
static void test_shift_q31_to_f32(const q31_t data, const uint32_t shift, const float32_t expected)
{
const float32_t shifted_data = Z_SHIFT_Q31_TO_F32(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
(double)shifted_data, (double)expected);
}
static void test_shift_q31_to_f64(const q31_t data, const uint32_t shift, const float64_t expected)
{
const float64_t shifted_data = Z_SHIFT_Q31_TO_F64(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
shifted_data, expected);
}
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q31_CASE, shift_q31_to_f32,
TEST_CORNER_CASES_SHIFT_Q31_TO_F32)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q31_CASE, shift_q31_to_f64,
TEST_CORNER_CASES_SHIFT_Q31_TO_F64)
ZTEST_SUITE(shift_q31, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 OWL Services LLC. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/dsp/utils.h>
#include "common/test_common.h"
#define DEFINE_MULTIPLE_TEST_CASES(constructor, variant, array) \
FOR_EACH_IDX_FIXED_ARG(constructor, (), variant, array)
#define RE_DEFINE_TEST_VARIANT3(...) DEFINE_TEST_VARIANT3(__VA_ARGS__)
#define DEFINE_SHIFT_Q7_CASE(n, args, func) \
RE_DEFINE_TEST_VARIANT3(shift_q7, func, n, __DEBRACKET args)
#define TEST_CORNER_CASES_SHIFT_Q7_TO_F32 \
(-128, 0, -1.0F), (127, 0, 0.9921875F), (127, 7, 127.0F), (-128, 7, -128.0F)
#define TEST_CORNER_CASES_SHIFT_Q7_TO_F64 \
(-128, 0, -1.0), (127, 0, 0.9921875), (127, 7, 127.0), (-128, 7, -128.0)
static void test_shift_q7_to_f32(const q7_t data, const uint32_t shift, const float32_t expected)
{
const float32_t shifted_data = Z_SHIFT_Q7_TO_F32(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
(double)shifted_data, (double)expected);
}
static void test_shift_q7_to_f64(const q7_t data, const uint32_t shift, const float64_t expected)
{
const float64_t shifted_data = Z_SHIFT_Q7_TO_F64(data, shift);
zassert_equal(shifted_data, expected,
"Conversion failed: 0x%08x shifted by %d = %f (expected %f)", data, shift,
shifted_data, expected);
}
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q7_CASE, shift_q7_to_f32, TEST_CORNER_CASES_SHIFT_Q7_TO_F32)
DEFINE_MULTIPLE_TEST_CASES(DEFINE_SHIFT_Q7_CASE, shift_q7_to_f64, TEST_CORNER_CASES_SHIFT_Q7_TO_F64)
ZTEST_SUITE(shift_q7, NULL, NULL, NULL, NULL, NULL);

View file

@ -0,0 +1,6 @@
tests:
zdsp.utils:
integration_platforms:
- native_sim
tags:
- zdsp