Concepts First
- DSP library: a set of optimized math functions (filtering, FFT, matrices, trigonometry) that are faster than hand-written loops — the foundation of signal processing.
- Q15/Q31 fixed point: embedded systems often avoid floating point, representing fractions with 16/32-bit integers;
q31means the 1.31 fixed-point format. - FIR filter: finite impulse response filter; the output is a weighted sum of recent inputs, widely used for denoising and shaping.
- FFT (Fast Fourier Transform): converts a time-domain signal to the frequency domain;
rfftis the real-input variant for spectrum analysis. - Reference comparison: each case has a precomputed
ref_resultarray and only prints success when outputs match, proving the algorithm implementation is correct.
Example Overview
This page is based on the dsp example in the official Bouffalo SDK (examples/dsp), which demonstrates the RISC-V DSP math library (csi_math):
main.ccompiles the 5 case files undercases/into one binary using a macro-rename trick and calls them in sequence;- Each case computes from the
srcinput array and compares point by point againstref_result; a mismatch prints failure and halts; - Covers 5 typical algorithms: FIR filtering, matrix multiply, real FFT, sine, variance;
- When all pass it prints
Example all successfully!and enters a dead loop. - Typical uses: audio equalization/denoising (FIR), vibration spectrum analysis (FFT), sensor calibration (matrix/variance).
Note
BL616/BL618 RISC-V cores support DSP extension instructions, which the SDK's csi_math library uses automatically — this page validates that hardware-accelerated math path.
Operation Steps
Open a terminal and enter the SDK DSP example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/dspRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open a serial tool (baud rate 2000000). The example runs 5 algorithm checks in sequence: FIR filter (riscv_dsp2_fir_q15), matrix multiply (matrix_q31), real FFT (rfft_q15), sine (sin_q31), variance (variance_q15). Each compares its output against precomputed reference values; when all pass it prints Example all successfully!.
Code Execution Flow
The flow from boot to verification is:
APIs Used by the Example
csi_sin_q31(x)
Computes sine with Q31 fixed-point input/output; the example verifies 128 input points against reference values.
Parameters:
x: angle in Q31 format
Returns: sine value in Q31 format
riscv_dsp2_fir_q15 / riscv_dsp2_rfft_q15, etc.()
Entry functions of each case in cases/ (renamed by macros in main.c); they use the FIR/FFT/matrix/variance APIs from csi_math.h and compare against ref_result.
Parameters: none
Returns: 0; on failure it halts in while(1)
csi_math.h(header)
The DSP library's public header, declaring csi_sin_q31, csi_fir_q15, csi_rfft_q15, csi_mat_q31, csi_var_q15, etc. Include "csi_math.h" in your project to use them.
Parameters: —
Returns: —
Complete Code
The following is the complete source of dsp/main.c and one case (riscv_dsp2_sin_q31.c), identical to the official example, collapsed by default (the other 4 cases have the same structure and live in cases/):
📜 Click to expand dsp/main.c full code
#include "bflb_mtimer.h"
#include "board.h"
#define DBG_TAG "MAIN"
#include "log.h"
#define main riscv_dsp2_fir_q15
#define ref_result ref_result_riscv_dsp2_fir_q15
#define result result_riscv_dsp2_fir_q15
#define src src_riscv_dsp2_fir_q15
#include "cases/riscv_dsp2_fir_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE
#define main riscv_dsp2_matrix_q31
#define ref_result ref_result_riscv_dsp2_matrix_q31
#define result result_riscv_dsp2_matrix_q31
#define src src_riscv_dsp2_matrix_q31
#include "cases/riscv_dsp2_matrix_q31.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE
#define main riscv_dsp2_rfft_q15
#define ref_result ref_result_riscv_dsp2_rfft_q15
#define result result_riscv_dsp2_rfft_q15
#define src src_riscv_dsp2_rfft_q15
#include "cases/riscv_dsp2_rfft_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE
#define main riscv_dsp2_sin_q31
#define ref_result ref_result_riscv_dsp2_sin_q31
#define result result_riscv_dsp2_sin_q31
#define src src_riscv_dsp2_sin_q31
#include "cases/riscv_dsp2_sin_q31.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE
#define main riscv_dsp2_variance_q15
#define ref_result ref_result_riscv_dsp2_variance_q15
#define result result_riscv_dsp2_variance_q15
#define src src_riscv_dsp2_variance_q15
#include "cases/riscv_dsp2_variance_q15.c"
#undef main
#undef ref_result
#undef result
#undef src
#undef TEST_SIZE
int main(void)
{
board_init();
printf("Example run riscv_dsp2_fir_q15!\r\n");
riscv_dsp2_fir_q15();
printf("Example run riscv_dsp2_matrix_q31!\r\n");
riscv_dsp2_matrix_q31();
printf("Example run riscv_dsp2_rfft_q15!\r\n");
riscv_dsp2_rfft_q15();
printf("Example run riscv_dsp2_fir_q15!\r\n");
riscv_dsp2_sin_q31();
printf("Example run riscv_dsp2_variance_q15!\r\n");
riscv_dsp2_variance_q15();
printf("Example all successfully!\r\n");
while (1) {
bflb_mtimer_delay_ms(1000);
}
}📜 Click to expand cases/riscv_dsp2_sin_q31.c full code
#include <stdio.h>
#include <math.h>
#include "csi_math.h"
#define TEST_SIZE 128
static q31_t src[TEST_SIZE] = {
0x71f40eb8, 0x1aedfac4, 0x0f3ff639, 0x40eb378a, 0x159f1974, 0x5428ca42, 0x3e990f9d, 0x209a58e6,
0x62875be9, 0x5816212a, 0x28a6ff13, 0x7a51f6a7, 0x3ca51dcd, 0x052989e9, 0x613a6877, 0x413bbd0c,
0x502a2298, 0x44af92d2, 0x1851b3ba, 0x531d1932, 0x5259e962, 0x36537413, 0x457bf10d, 0x6f43906c,
0x473fcc60, 0x3ed09220, 0x2a151a9b, 0x12b52c1b, 0x3f79f6a8, 0x0aea56a5, 0x7f5ab471, 0x0ee05125,
0x2ec0f1de, 0x2b59847b, 0x6da9fe05, 0x0aa207c9, 0x4e0db117, 0x1ad037ab, 0x78555489, 0x2005ef5a,
0x107b129e, 0x36df9796, 0x4687e80b, 0x732d0ac1, 0x534c3c63, 0x1c0f8e39, 0x30b19f31, 0x19f18910,
0x3850eca4, 0x10118818, 0x45a0df62, 0x4104e5c3, 0x14195691, 0x74162b17, 0x5b9f737e, 0x445d0125,
0x1df54a61, 0x788e5af7, 0x3bc0bd34, 0x0f07e8f3, 0x211f4d64, 0x31a834bb, 0x506dd274, 0x6bb74d4b,
0x66e132bc, 0x263e92dd, 0x5cb47ec9, 0x541f4af5, 0x2320126d, 0x58b7700e, 0x00d0828f, 0x1182b519,
0x3d33cf33, 0x6577c618, 0x22af9e0f, 0x058ffea4, 0x2bf32d25, 0x07fb97e5, 0x7895e5f5, 0x73b5f4dd,
0x0399834c, 0x52f68eac, 0x66311a01, 0x3690ee7d, 0x1258b0e3, 0x1d85a331, 0x28e0511a, 0x1485ae28,
0x556e56c5, 0x690aa18a, 0x040672e5, 0x5c14209e, 0x5d8ff577, 0x61ba1095, 0x27c2e01e, 0x797a7a8f,
0x2d957473, 0x2a0c920a, 0x5b8d5230, 0x4c5e6a70, 0x62c33c82, 0x552b8cf0, 0x52a5adae, 0x3023134c,
0x2015d046, 0x734945c9, 0x73233bd0, 0x4e0a9921, 0x52fc390d, 0x02b26459, 0x25ea9d61, 0x4197a318,
0x55b9ca53, 0x6eb23e44, 0x31862277, 0x7b8f7be8, 0x2c0cc008, 0x533a8b83, 0x6a35c724, 0x0a232bc0,
0x2e6247d9, 0x3b442980, 0x1de30e64, 0x749b41b2, 0x70834a66, 0x01f8428b, 0x56356d0a, 0x4708a91b
};
static q31_t ref_result[TEST_SIZE] = {
0xae927d72, 0x7c0dcc9a, 0x571dc1b4, 0xfa3a9ce8, 0x6fbe4766, 0x950553ac, 0x08cd75d4, 0x7ff10d88,
0x80fc5f3e, 0x8989e528, 0x74a02914, 0xdcc60f08, 0x14fc107a, 0x2016c0b4, 0x803bb9f2, 0xf841608c,
0xa4c37a12, 0xe2d13fdc, 0x7701f3ce, 0x98c47cba, 0x9babda5e, 0x3a861816, 0xddf4f932, 0xa247ba02,
0xd36834fe, 0x0771665a, 0x70a392a6, 0x65b47542, 0x034a13ca, 0x4158f3d2, 0xfbf19cc6, 0x55615368,
0x5fdfbdea, 0x6ca44370, 0x9bbb0ff4, 0x3fd0ad96, 0xae8a0976, 0x7bdf3ff8, 0xd0f56b9a, 0x7fffc56a,
0x5c9ea4d4, 0x3771a934, 0xd7aa340e, 0xb4a477aa, 0x9816491c, 0x7d9cd9ea, 0x575fbe36, 0x7a621b14,
0x2f245878, 0x5acfe2d2, 0xdd15a2fa, 0xf9996cf4, 0x6ac53b20, 0xb9575c24, 0x82f21454, 0xe4cb524a,
0x7f5b34f4, 0xd2434f50, 0x1a7e13a4, 0x561a435e, 0x7fcdac80, 0x52dadf6a, 0xa39b2218, 0x94978822,
0x873b677e, 0x7a08961a, 0x81ac072c, 0x95262c98, 0x7e7e8b94, 0x8818541c, 0x051dbcf2, 0x60f7405a,
0x1185c64c, 0x849581a0, 0x7ee31fd6, 0x2284557a, 0x6a99f2be, 0x30e22044, 0xd26fa1a0, 0xb76248d4,
0x167fd962, 0x9954214a, 0x85de5864, 0x392d8c3e, 0x644f642a, 0x7f0db084, 0x7409edc4, 0x6c371202,
0x90d846b8, 0x8c670202, 0x19206920, 0x825db7b6, 0x80eac460, 0x8075db58, 0x76d279d2, 0xd7b86778,
0x6494f30e, 0x70bd232c, 0x830a5b0a, 0xb6f92e88, 0x812cf66e, 0x91aab728, 0x9a86f676, 0x59e5afda,
0x7fff28b2, 0xb53400a0, 0xb472be58, 0xae98fc86, 0x993ebfe8, 0x10e525f8, 0x7aa33c5c, 0xf60164b2,
0x8feff520, 0x9fe370c6, 0x537dd3cc, 0xe4538b3e, 0x6a40ae5e, 0x98572fc8, 0x8fbe4fce, 0x3d18f9cc,
0x61655134, 0x1d79d4fc, 0x7f4f471a, 0xbc1671de, 0xa7cc325e, 0x0c5b6644, 0x8e80c89c, 0xd4adac08
};
int main(void)
{
q31_t result;
int i;
for (i = 0; i < TEST_SIZE; i++) {
result = csi_sin_q31(src[i]);
if (result != ref_result[i]) {
printf("Example run failed!\r\n");
while (1)
;
}
}
printf("Example run successfully!\r\n");
return 0;
}FAQ
\"Example run failed!\" and the program hangs?
A case's output differs from its reference values. Usually the case files were modified or the build optimized unexpectedly; restore the cases/ directory and rebuild — normally all 5 groups pass.
Why Q15/Q31 instead of float?
Fixed point needs no FPU (or avoids slow software floats), is faster, and gives deterministic results — ideal for real-time signal processing. Use Q formats for fractions (q31 is 1.31) and shift after operations to restore scale.
How do I use the DSP library in my own project?
Enable the DSP library in defconfig (e.g., CONFIG_DSP=y per the SDK component docs), then #include "csi_math.h" and call csi_sin_q31, csi_fir_q15, csi_rfft_q15, etc.
Can these cases become real applications?
Yes. Replace src with your sampled data and turn the ref_result comparison into business logic — e.g., feed microphone samples into csi_fir_q15 for denoising, then output to I2S playback.

