Sida 1 av 3

Fungerar va_arg med float* ?

Postat: 7 februari 2025, 23:43:57
av DanielM
Jag försöker lösa differentialsekvationer på ARM processorer då jag håller på med att lösa diskreta Algebraiska Riccati Ekvationer där P är lösningen.

\(P = A^\top P A -(A^\top P B)(R + B^\top P B)^{-1}(B^\top P A) + Q.\)

Jag tänkte att jag ger er .c kod och ett arbetsexempel till att börja med. Detta exempel löser alltså ett massa-dämpare-fjäder system med hjälp av fjärde ordningen av runge-kutta.

Det som är unik med denna kod är att den använder sig av variadic. Detta betyder att man kan variabelt ha utökade argument om så önskas. Jajjemän, precis som Matlab och Python så stöder C detta ända sedan tidernas begynnelse från ANSI C (1989). :mrgreen:

Men ett problem! Jag får hardfault när jag kommer till denna kodrad - Varför då? Kan inte variadic köra floatpekare? Min kompilator är GCC för ARM. För MSVC så fungerar det utan problem.

Kod: Markera allt

matrices[i] = va_arg(args, float*);
Körbart exempel här: https://onlinegdb.com/2c6Q1hR-ZB

Kod: Markera allt

/******************************************************************************

                            Online C Compiler.
                Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/


#include <string.h>						/* For memcpy, memset etc */
#include <stdio.h>						/* For printf */
#include <stdlib.h>						/* Standard library */
#include <math.h>						/* For sqrtf */
#include <float.h>						/* Required for FLT_EPSILON, FLT_MAX, FLT_MIN */
#include <stddef.h>						/* Requried for size_t,  NULL etc... */
#include <time.h> 						/* For srand, clock */
#include <stdarg.h>                     /* For ... arguments */

void print(const float A[], const size_t row, const size_t column) {
	size_t i, j;
	for (i = 0; i < row; i++) {
		for (j = 0; j < column; j++) {
			printf("%0.7f\t", *(A++));
		}
		printf("\n");
	}
	printf("\n");
}

void mul(const float A[], const float B[], float C[], const size_t row_a, const size_t column_a, const size_t column_b){
    
    /* Decleration */
	size_t i, j, k;

	/* Data matrix */
	const float* data_a, * data_b;

	for (i = 0; i < row_a; i++) {
		/* Then we go through every column of b */
		for (j = 0; j < column_b; j++) {
			data_a = A;
			data_b = &B[j];

			C[0] = 0.0f; /* Reset */
			/* And we multiply rows from a with columns of b */
			for (k = 0; k < column_a; k++) {
				*C += data_a[0] * data_b[0];
				data_a++;
				data_b += column_b;
			}
			C++; /* ;) */
		}
		A += column_a;
	}
}

 /*
  * Fourth-order Runge–Kutta method.
  * Runge-Kutta method is not the best ODE solver, but it's the best ODE solver for fixed step time iteration.
  * h - Step time
  * Y[iterations*N] - Output
  * y[N] - Initial state vector
  * N - Dimension for y-vector
  * odefun(const float t, float y[], const float* matrices[], const size_t rows[], const size_t columns[])
  * ... = const float matrixA[], const size_t rowA, const size_t columnA, const float matrixB[], const size_t rowB, const size_t columnB, const float matrixC[], const size_t rowC, const size_t columnC, etc...
  */
void rk4args(const size_t iterations, const float h, float Y[], float y[], const size_t N, void (*odefun)(float, float*, float**, const size_t*, const size_t*), const size_t number_of_pointers, ...) {
	/* Variables */
	size_t i, j;

	/* Constants */
	const size_t length = N * sizeof(float);

	/* Create vectors */
	float* k1 = (float*)malloc(length);
	float* k2 = (float*)malloc(length);
	float* k3 = (float*)malloc(length);
	float* k4 = (float*)malloc(length);

	/* Temporary vectors */
	float* yt = (float*)malloc(length);

	/* Variables */
	float t = 0.0f;

	/* Initial output */
	memcpy(Y, y, length);

	/* Variable arguments */
	va_list args;
	va_start(args, number_of_pointers);

	/* Pointers */
	float** matrices = (float**)malloc(number_of_pointers * sizeof(float*));
	size_t* rows = (size_t*)malloc(number_of_pointers * sizeof(size_t));
	size_t* columns = (size_t*)malloc(number_of_pointers * sizeof(size_t));

	/* Get the arguments */
	for (i = 0; i < number_of_pointers; i++) {
		matrices[i] = va_arg(args, float*);
		rows[i] = va_arg(args, const size_t);
		columns[i] = va_arg(args, const size_t);
	}

	/* Perform Runge-Kutta */
	for (i = 1; i < iterations; i++) {
		/* Receive old output */
		for (j = 0; j < N; j++) {
			y[j] = Y[(i - 1) * N + j];
		}

		/* First statement */
		memcpy(yt, y, length);
		odefun(t, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k1[j] = yt[j] * h;
		}

		/* Second statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k1[j] / 2.0f;
		}
		odefun(t + h / 2.0f, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k2[j] = yt[j] * h;
		}

		/* Third statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k2[j] / 2.0f;
		}
		odefun(t + h / 2.0f, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k3[j] = yt[j] * h;
		}

		/* Fourth statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k3[j];
		}
		odefun(t + h, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k4[j] = yt[j] * h;
		}

		/* Save output */
		for (j = 0; j < N; j++) {
			Y[i * N + j] = y[j] + (k1[j] + 2.0f * k2[j] + 2.0f * k3[j] + k4[j]) / 6.0f;
		}

		/* Update t */
		t += h;
	}

	/* Free */
	free(k1);
	free(k2);
	free(k3);
	free(k4);
	free(yt);
	free(rows);
	free(columns);
	free(matrices);

	/* Close */
	va_end(args);
}

 /* Constants */
#define N 2
#define h 0.1f
#define row_a 2
#define column_a 2
#define row_b 2
#define column_b 1
#define row_u 1
#define column_u 1
#define ITERATIONS 200

/* Create ODE equation */
void odefun(const float t, float y[], float* matrices[], const size_t rows[], const size_t columns[]) {
    /* Get the matrices */
    float* A = matrices[0];
    float* B = matrices[1];
    float* u = matrices[2];

    /* Get the sizes */
    size_t row_A = rows[0];
    size_t column_A = columns[0];
    size_t row_B = rows[1];
    size_t column_B = columns[1];
    size_t row_U = rows[2];
    size_t column_U = columns[2];

    /* Solve y = A*y + B*u */
    float Ay[2];
    float Bu[2];
    mul(A, y, Ay, row_A, column_A, 1);
    mul(B, u, Bu, row_B, column_B, 1);
    size_t i;
    for (i = 0; i < row_A; i++) {
        y[i] = Ay[i] + Bu[i];
    }
}

int main() {
    clock_t start, end;
    float cpu_time_used;
    start = clock();

    /* Declare intial state */
    float y0[N] = { 0 };

    /* Declare output */
    float Y[ITERATIONS * N];

    /* Declare number of pointers */
    const size_t number_of_pointers = 3;
    const float k = 5.0f;    /* Spring constant [N/m] */
    const float b = 1.4f;    /* Damper constant [Ns/m] */
    const float m = 2.0f;    /* Mass [kg] */
    const float F = 8.0f;    /* Force [N] */
    const float A[row_a * column_a] = { 0, 1, -k / m, -b / m };
    const float B[row_b * column_b] = { 0, 1 / m };
    const float u[row_u * column_u] = { F };

    /* Perform Runge-Kutta 4:th order with extended arguments ... of const float[], const size_t, const size_t etc... */
    rk4args(ITERATIONS, h, Y, y0, N, odefun, number_of_pointers, A, (const size_t)row_a, (const size_t)column_a, B, (const size_t)row_b, (const size_t)column_b, u, (const size_t)row_u, (const size_t)column_u);

    end = clock();
    cpu_time_used = ((float)(end - start)) / CLOCKS_PER_SEC;
    printf("\nTotal speed  was %f\n", cpu_time_used);

    /* Plot */
    print(Y, ITERATIONS, N);

    return EXIT_SUCCESS;
}

Re: Fungerar va_arg med float* ?

Postat: 8 februari 2025, 08:53:39
av Findecanor
Vilken slags ARM-plattform? Om du kör på ett litet litet inbyggt system kanske malloc() egentligen inte finns, och alltid ger NULL.

Re: Fungerar va_arg med float* ?

Postat: 8 februari 2025, 09:30:27
av DanielM
Det är STM32. Malloc finns.

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 14:35:36
av DanielM
Nu har jag hittat ett fel här!
Under körningen så blir k3 NULL....
Har jag verkligen förbrukat upp alla mina 8 kB i RAM redan???

Tror jag vet den misstänkta!

Kod: Markera allt

void rk4args(const size_t iterations, const float h, float Y[], float y[], const size_t N, void (*odefun)(float, float*, float**, const size_t*, const size_t*), const size_t number_of_pointers, ...) {
	/* Variables */
	size_t i, j;

	/* Constants */
	const size_t length = N * sizeof(float);

	/* Create vectors */
	float* k1 = (float*)malloc(length);
	float* k2 = (float*)malloc(length);
	float* k3 = (float*)malloc(length);
	if(k3 == NULL){
		y[0] = 1;
		return;
	}
	float* k4 = (float*)malloc(length);

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 19:09:14
av DanielM
Tydligen så har jag slut på RAM. Därav felet.
Får väll deklarera statiskt då.

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 19:18:44
av hawkan
Ja 8 kbyte som räcker till så mycket.
Räkna igenom hur mycket minne du förbrukar. Det finns väl något sätt.
Går det inte dynamiskt lär det inte gå statiskt heller.

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 20:17:24
av TomasL
Dynamisk allokering är väl något man skall undvika så lång det går, framförallt i inbäddade system. Jag lärde mig en gång att dynamisk allokering är förbjudet i inbäddade system.
Har för mig att MISRA dessutom förbjuder det.
Med statisk allokering, får man förhoppningsvis en kompilatorvarning om man allokerar för mycket.

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 20:37:58
av Findecanor
Ja, försöka att allockera statiskt, med maximal storlek på arrayerna från början.

Du kanske också har stött på fragmentering eller onödig padding.
Du skulle också kunna försöka allockera allt i en klump.
T.ex om du ska ha fyra arrayer:

Kod: Markera allt

float *a = (float *)malloc(N * sizeof(float) * 4);
float *b = a + N;
float *c = b + N;
float *d = c + N;

Re: Fungerar va_arg med float* ?

Postat: 9 februari 2025, 20:41:42
av TomasL
Grejjen är väl, att man måste ha 1000% koll på tillgängligt RAM, om man använder dynamisk allokering, i alla lägen.
Eftersom inbäddade system är kraftigt begränsade, måste man då räkna på alla scenarier, för att se att man inte allokerar för mycket, vilket inte alltid är helt lätt.
Därför skall man aldrig någonsin använda dynamisk allokering i inbäddade system.

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 00:09:20
av DanielM
TomasL skrev: 9 februari 2025, 20:17:24 Dynamisk allokering är väl något man skall undvika så lång det går, framförallt i inbäddade system. Jag lärde mig en gång att dynamisk allokering är förbjudet i inbäddade system.
Har för mig att MISRA dessutom förbjuder det.
Med statisk allokering, får man förhoppningsvis en kompilatorvarning om man allokerar för mycket.
Förbjudet är det inte att använda dynamisk allokering. I så fall vore funktionen bortblockad från C :mrgreen: Men jag förstår riskerna med dynamisk minnesallokering.

Jag har börjat med att exkludera .c filer från mitt projekt. Då har dom gått från blodröd till grön. Men nu måste jag titta mera på vad jag kan göra.
Statisk minnesallokering låter som en lösning. Men då kommer jag behöva skriva om mycket av min kod. Finns det inget smart sätt man kan göra för att få kompilatorn att tolka malloc på något annat sätt än dynamisk allokering?

En lösning som skulle fungera för mig är att skriva om t.ex.

Kod: Markera allt

float* matris = (float*)malloc(row * column * sizeof(float)); 
Till
float matrix[5*5]; // Om row == column == 5
Är det då Flash som förbrukas då?
Skärmbild 2025-02-10 000338.png

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 06:13:33
av TomasL
Förbjudet är det inte att använda dynamisk allokering. I så fall vore funktionen bortblockad från C :mrgreen: Men jag förstår riskerna med dynamisk minnesallokering.
Enligt MISRA, en standard som alla borde följa, är det inte tillåtet med dynamis allokering.

Direktiv 4.12 i MISRA C:2012, denna regel finns i samtliga utgåvor av MISRA
Dir 4.12 Dynamic memory allocation shall not be used
Category Required

Amplification
This rule applies to all dynamic memory allocation packages including:
• Those provided by The Standard Library;
• Third-party packages.

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 06:17:04
av hawkan
Skrivet innan TomasLs svar.
Nej det är RAM som används i båda fallen. Både stack och heap använder ram.
Kolla hur mycket minne som reserverats för heap, där malloced minne tas från.
Förbjudet är det väl inte, men använd inte malloc/free ändå. Uppenbarligen
är det ett problem i detta fallet, trots din förståelse av problemet med malloc.
Är det alltid en viss storlek på dina matriser så speca storleken som du gör,
fast med pre-processor variabler för storlek.

Matriser, matrisoperationer och flyttal är ökända minnesslukare.
8 kB är pyttelite för sånt och man får verkligen anstränga sej för att få plats.
Sedan att iterera fram en lösning det är heller inte bra för det blir variation
i hur lång tid det tar. Så du har lite utmaningar med detta.

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 08:46:13
av DanielM
Kan man deklarera en array i en funktion på Flash?
Typ om jag skriver

Kod: Markera allt

static float A[5*5];
Eller måste jag använda nyckelordet const också? Då blir arrayen oanvändbar för mig.

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 08:49:30
av TomasL
Nej, du kan naturligtvis inte ha variabler i flashminnet.
Konstanter hamnar i Flash, variabler måste av naturliga skäl ligga i RAM.

Re: Fungerar va_arg med float* ?

Postat: 10 februari 2025, 09:06:57
av DanielM
Hmm....
Jag har en pekararray i en struktur. Den initialiseras bara en gång. Sedan aldrig mer.