STM32N6570-DK: Inference always outputs the same class with the same confidence level

Question/Issue:
The inference result is always fixed to class 3 with a confidence of around ~0,996. When further investigated, I found that the features array is always (mostly) full of zero’s.

Project ID:
971849

Context/Use case:
I am building an algorithm to detect 9 different cylindrical objects. The microphone that’s on the DK picks up the sound, a DMA puts the samples in a uint16_t buffer. I then convert the buffer using q15_arm_to_float(); and after that I use signal_from_buffer() and call run_classifier()

Steps Taken:

  1. I enabled the debug and printed the results to UART, the Features calculation takes 0ms and the output after that shows the array filled with 0’s, before it had some float values, but most where still 0
  2. Confirmed the buffer for the raw microhpone data is not in the Dcache of the CPU.
  3. Confirmed that the float buffer that is being put into the signal_to_buffer function is also correct.

Expected Outcome:
The classifier should output different labels depending on the sound the microphone picks up.

Actual Outcome:
The classifier always outputs class 3

Reproducibility:

  • Always

Environment:

  • Platform: STM32N6570-DK, on-board microphone
  • Build Environment Details: STM32CubeIDE 2.0.0
  • OS Version: Windows 11
  • Edge Impulse Version (Firmware): N/A I am using my own Firmware, this firmware is coded in C
  • Edge Impulse CLI Version: N/A, see above
  • Project Version: Version 3
  • Custom Blocks / Impulse Configuration: [Describe custom blocks used or impulse configuration]
    Logs/Attachments:
    The output of the model in UART:
    Features (0 ms.): a bunch of zero’s here
    Running impulse…
    [0] Drinkbeker_0: 0.000000
    [1] Drinkbeker_100: 0.000000
    [2] Drinkbeker_50: 0.000000
    [3] Glas_0: 0.996094
    [4] Glas_100: 0.000000
    [5] Glas_50: 0.000000
    [6] MetalenBeker_0: 0.000000
    [7] MetalenBeker_100: 0.000000
    [8] MetalenBeker_50: 0.000000
    Segment 0: Glas_0 (99.61%)

Additional Information:
The model works fine when running it on my laptop, it defaults to label 3 when doing nothing, but that is to be expected i think.
Here is the relevant code that calls the run_classifier function:

                //Reset ArrayIndex
				ArrayIndex = 0;


				// Process all 10 segments
				uint32_t valid_results = 0;

				c_run_classifier_init();

				// Process all 10 segments
				for(int i = 0; i < 10; i++)
				{
					// Convert this chunk of uint16_t Q15 to float32
					arm_q15_to_float((q15_t *)&PcmBuffer[ArrayIndex], audio_float_buffer, 3200);

					SignalToBufferResult = c_signal_from_buffer(audio_float_buffer, 3200, Signal);

					if(SignalToBufferResult != 0)
					{
						UART_broadcast("Writing to signal buffer failed (segment %d), error code: %d\n", i, SignalToBufferResult);
						ArrayIndex += 3200;
						continue;
					}

					//Run the AI on the current segment
					AIresult = c_run_classifier(Signal, AI200msResult, true);
                    ArrayIndex += 3200;
                }

Yesterday I found out that you can use C functions directly instead of writing your own wrapper, if you defined some statements (EI_C_LINKAGE=1 and EIDSP_SIGNAL_C_FN_POINTER=1). So I did just that. The code now looks like this:

				//Reset ArrayIndex
				ArrayIndex = 0;

				// Process all 10 segments
				uint32_t valid_results = 0;

				// Process all 10 segments
				for(int i = 0; i < 10; i++)
				{
					// Convert this chunk of uint16_t Q15 to float32
					arm_q15_to_float((q15_t *)&PcmBuffer[ArrayIndex], features, 3200);

					signal_t signal;
					signal.total_length = 3200;
					signal.get_data = &get_feature_data;

					ei_impulse_result_t result;

					//Run the AI with the current buffer
					EI_IMPULSE_ERROR res = run_classifier(&signal, &result, true);

					if(res != EI_IMPULSE_OK)
					{
						UART_broadcast("Executing model failed in segment: %d, error code: %d\n", i, res);
					} else {
						UART_broadcast("Model execution succesfull of segment: %d", i);
						result_confidences[valid_results] = result.classification[0].value; // test code
						result_labels[valid_results] = result.classification[0].label;      // Test code
						valid_results++;
					}
					ArrayIndex += 3200;
				}

It now returns a mostly zero array of features, but most of the time there are at least a couple of values, but most importantly, it now returns:

ERR: Failed to allocate scratch buffer of size 64, reached EI_MAX_SCRATCH_BUFFER_COUNT
Failed to initialize the model (error code 1)
Executing model failed in segment: 0, error code: -6

So I tried increasing the EI_MAX_SCRATCH_BUFFER_COUNT to 8, then 128, and even 256, but that does not really do anything, I debugged the code and found that this ERR message comes from RequestScratchBufferInArenaImpl(); In there, the value of scratch_buffers_ix is way too big, it is always somewhere around 1043296727 or 3191845335. So that is where I am at now, I just can’t seem to find the cause of why scratch_buffers_ix is so big.

Also, the get_feature_data function looks like this:

int get_feature_data(size_t offset, size_t length, float *out_ptr){
	memcpy(out_ptr, features + offset, length * sizeof(float));
	return 0;
}

Hi @Ko1rr1ef

did you tested the model using the example standalone with a static feature ?

in this way you have a much simpler project to test if the model is working as expected.

you can enable some debug print here

setting true instead of false as third parameter.

I’ll have a quick look at your project.

fv

Thanks for the reply fv,

I am trying to compile the standalone example, but during compilation it returns a lot of errors, some of these are:

Model/network.c:57:4: error: #error "Possible mismatch in ll_aton library used"
   57 | #  error "Possible mismatch in ll_aton library used"
      |    ^~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_3':
Model/network.c:348:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  348 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_4':
Model/network.c:716:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  716 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:767:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  767 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:819:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  819 |     .vshift = 0,
      |      ^~~~~~
Model/network.c:871:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
  871 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_5':
Model/network.c:1263:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
 1263 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_Start_EpochBlock_7':
Model/network.c:1696:6: error: 'LL_Convacc_InitTypeDef' has no member named 'vshift'
 1696 |     .vshift = 0,
      |      ^~~~~~
Model/network.c: In function 'LL_ATON_EpochBlockItems_network':

Also I just noticed, all this time I was using the C++ library and not the neural-art library, I also tried integrating that into my project, but the generated code from edge-impulse misses a lot of ll_aton files, and I can’t seem to find that code anywhere

Hi @Ko1rr1ef

I updated the repo for the example-standalone, now should build

fv

Hi @ei_francesco

Thanks! It compiles now, btw the readme should be updated to instruct the user to include stai_network.h/.c now, had to put those in now.

So the features array that is now returned is fixed, it has data now, no full-on 0’s anymore, but it still returns Features (0 ms.): (rest of the data), so that is weird.

Also when it’s done outputting the feature results the last thing it spits out is: Running impulse…
After that nothing else happens. Also the program can’t be reset anymore with the reset button, the board has to be power reset for the program to load again.

This is the part of my ei_main.cpp that is important here:

do {
    ei_sleep(2000);
    ei_printf("Running impulse...\n");
    // invoke the impulse
    EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result, true);

    ei_printf("run_classifier returned: %d\n", res);

    if (res != 0) {
        ei_printf("Error while running classifier: %d\n", res);
        return 1;
    }

    display_results(&ei_default_impulse, &result);
    ei_sleep(2000);
}while(1);

So it never executes ei_printf(“run_classifier returned: %d\n”, res);

I will try debugging the software a bit in the meantime, let me know if you have any further steps I could take.

~Kyan

Hi @Ko1rr1ef ,

Probably the inference get stuck, I need to debug it, but I don’t have the board with me right now, I can have a look next week.
Let me know if you find anything else in the meantime.

regards,
fv

Hi @ei_francesco ,

Ok thanks for taking the time! In the meantime I am trying to use the edge-impulse library as dsp only and creating my own generated code using the up to date STedgeAI generation tool. I am currently debugging that, but please let me know when you find something!

Best regards,
Kyan

Hi @ei_francesco,

I’m having some trouble using the STEdgeAI generation tool. While the generation itself works fine, porting the Edge Impulse DSP functions is proving a bit too challenging for me.
Please let me know if you need anything from my end to help debug the application on the board!

Best regards,
Kyan Vijlbrief

Hi @Ko1rr1ef

I’m debugging to check what’s wrong on our engine.
I’ll keep you updated!

fv

Hi @ei_francesco,

Thanks for taking the time! Did you find anything while debugging the engine last week?

Best regards,
Kyan

Hi @Ko1rr1ef

not yet, I see the NPU is never returning done, but i don’t understand why… I’ll keep you posted

fv

Hi @Ko1rr1ef

there were some issue in the example-standalone, now should be working correctly!

with your project, for this sample Login - Edge Impulse, i get this:

Timing: DSP 12 ms, inference 2 ms, anomaly 0 ms, postprocessing 1 us
#Classification predictions:
AchtergrondRuis: 0.14062
Drinkbeker_0: 0
Drinkbeker_100: 0
Drinkbeker_50: 0
Geklik_muis: 0.85546
Glas_0: 0
Glas_100: 0
Glas_50: 0
Menselijke_spraak: 0.0039
MetalenBeker_0: 0
MetalenBeker_100: 0
MetalenBeker_50: 0

which is correct, so the model and the inference engine are working correctly.

let me know!
fv

Hi @ei_francesco,

It works indeed!! Thanks so much for helping me this far, really appreciate it.
I want to integrate this into my current project, which is generated by touchgfx, are there any configurations or memory configs I need to change to make the edge impulse code work in my own project?
Also I am curious, what ended up being the issue with the example code?

~ Kyan

Hi @Ko1rr1ef

The main changes on application side is how the xspi RAM/NOR is initialised:

then the issue i think was the weights were not read properly because the NPU was never returning (better said, signal DONE) when running the inference.

I think you need to check were weights are store in the external flash and check is not the same address were graphical objects are stored (it’s a looong time i’ve used touchgfx and I don’t recall anything :slight_smile: ), we store the weights at address 0x70180000, it’s network_data.hex.

I guess you have created your project with cubemx, so all the peripheral init are already done, you can probably just integrate this file example-standalone-st-stm32n6/edgeimpulse/ei_main.cpp at main · edgeimpulse/example-standalone-st-stm32n6 · GitHub in a thread.

If your project is in a public repo, feel free to link I can give a look.

regards,
fv

Hi @ei_francesco,

That explains a lot haha.

And yeah where the weights are stored is a bit of an issue. Currently I flash my application to 0x7010000, and it exceeds 0x70180000 (application is 619Kb). Touchgfx stores my data at 0x70200000 and above, so that’s fine.
I saw that the standalone example stores the application at 0x70080000, but that probably requires a bit of tinkering with the FSBL to load the application right? I haven’t done a lot with the FSBL when experimenting with this board. Can you help me with the FSBL part?

Unfortunately my code is not in a public repo, but I can provide extra code if needed!

~ Kyan

Hi @ei_francesco,

Nevermind, found it myself haha.
I integrated the edge impulse sdk and the functions into my project once again.
But when the codes calls the aton_neural_network_init from run_nn_inference, the contact with de debugger is lost and the code crashes.
I think this happens because I am also using a bit of the NPU ram for my program, which NPU RAM region does the edge impulse code use for running the neural networks?

Also I got 2 errors saying that the HAL_CACHEAXI_MspInit and MspDeInit were declared 2 times. I commented out the functions in npu_cache.c, could that also be causing issues?

After some further debugging, it crashes in LL_ATON_Init when executing the line:
t = ATON_CLKCTRL_CTRL_GET(0);

~ Kyan

Never mind, I missed the fact that there was an NPU_config() function in de main.c being called, copied that over and my network is now succesfully being called, the timing results are always 0ms so that is a bit strange, but I will look into that.

@ei_francesco I want to thank you for all the help the past couple weeks, couldn’t have done it without you! I will let you know if I need help with anything else :slight_smile:

2 Likes

Hi @Ko1rr1ef
glad you fixed it!

For FSBL or TouchGFX issue, better ask in the STM forum, they can provide better support.

fv